From 65778fa5e886c641422501e68ec7ffbe690abafc Mon Sep 17 00:00:00 2001 From: Takayuki Date: Thu, 22 Apr 2021 16:52:34 +0900 Subject: [PATCH 01/67] fix a false-positive inside const fn in comparison_chain --- clippy_lints/src/comparison_chain.rs | 14 +++++++++++++- tests/ui/comparison_chain.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index 42e153909ce75..150d8a3d6739e 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::implements_trait; use clippy_utils::{get_trait_def_id, if_sequence, is_else_clause, paths, SpanlessEq}; -use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_hir::{BinOpKind, Expr, ExprKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -64,6 +64,10 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { return; } + if parent_node_is_if_const_fn(cx, expr) { + return; + } + // Check that there exists at least one explicit else condition let (conds, _) = if_sequence(expr); if conds.len() < 2 { @@ -123,3 +127,11 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { fn kind_is_cmp(kind: BinOpKind) -> bool { matches!(kind, BinOpKind::Lt | BinOpKind::Gt | BinOpKind::Eq) } + +fn parent_node_is_if_const_fn(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + match cx.tcx.hir().find(cx.tcx.hir().get_parent_item(expr.hir_id)) { + Some(Node::Item(item)) => rustc_mir::const_eval::is_const_fn(cx.tcx, item.def_id.to_def_id()), + Some(Node::ImplItem(impl_item)) => rustc_mir::const_eval::is_const_fn(cx.tcx, impl_item.def_id.to_def_id()), + _ => false, + } +} diff --git a/tests/ui/comparison_chain.rs b/tests/ui/comparison_chain.rs index 3b03f8c7dfe7c..c12c6a310275d 100644 --- a/tests/ui/comparison_chain.rs +++ b/tests/ui/comparison_chain.rs @@ -203,4 +203,32 @@ mod issue_5212 { } } +enum Sign { + Negative, + Positive, + Zero, +} + +impl Sign { + const fn sign_i8(n: i8) -> Self { + if n == 0 { + Sign::Zero + } else if n > 0 { + Sign::Positive + } else { + Sign::Negative + } + } +} + +const fn sign_i8(n: i8) -> Sign { + if n == 0 { + Sign::Zero + } else if n > 0 { + Sign::Positive + } else { + Sign::Negative + } +} + fn main() {} From 02bf692169e1f2908169da8f870f8b324a4e6a17 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 22 Apr 2021 11:31:13 +0200 Subject: [PATCH 02/67] Merge commit '98e2b9f25b6db4b2680a3d388456d9f95cb28344' into clippyup --- .github/PULL_REQUEST_TEMPLATE.md | 5 +- .github/workflows/clippy_bors.yml | 13 +- CHANGELOG.md | 34 +- CONTRIBUTING.md | 6 +- clippy_lints/src/assertions_on_constants.rs | 5 +- clippy_lints/src/bool_assert_comparison.rs | 75 ++ clippy_lints/src/booleans.rs | 2 +- clippy_lints/src/checked_conversions.rs | 4 +- clippy_lints/src/collapsible_match.rs | 27 +- clippy_lints/src/comparison_chain.rs | 4 +- clippy_lints/src/copies.rs | 33 +- clippy_lints/src/deprecated_lints.rs | 68 +- clippy_lints/src/entry.rs | 697 +++++++++++++++--- clippy_lints/src/excessive_bools.rs | 6 +- clippy_lints/src/format.rs | 11 +- clippy_lints/src/formatting.rs | 17 +- clippy_lints/src/from_over_into.rs | 2 +- clippy_lints/src/if_then_some_else_none.rs | 11 +- clippy_lints/src/implicit_hasher.rs | 7 +- clippy_lints/src/implicit_saturating_sub.rs | 43 +- .../src/inconsistent_struct_constructor.rs | 2 + clippy_lints/src/infinite_iter.rs | 4 +- clippy_lints/src/lib.rs | 63 +- .../src/loops/explicit_into_iter_loop.rs | 13 +- clippy_lints/src/loops/explicit_iter_loop.rs | 10 +- clippy_lints/src/loops/manual_flatten.rs | 11 +- clippy_lints/src/loops/mod.rs | 29 +- clippy_lints/src/loops/never_loop.rs | 2 +- clippy_lints/src/macro_use.rs | 1 + clippy_lints/src/manual_map.rs | 96 +-- clippy_lints/src/manual_ok_or.rs | 9 +- clippy_lints/src/manual_unwrap_or.rs | 25 +- clippy_lints/src/map_identity.rs | 4 +- clippy_lints/src/matches.rs | 300 ++++++-- clippy_lints/src/mem_replace.rs | 24 +- .../src/methods/cloned_instead_of_copied.rs | 38 + clippy_lints/src/methods/filter_flat_map.rs | 18 - .../src/methods/filter_map_flat_map.rs | 18 - .../src/methods/filter_map_identity.rs | 12 +- clippy_lints/src/methods/filter_map_map.rs | 17 - clippy_lints/src/methods/flat_map_identity.rs | 18 +- clippy_lints/src/methods/flat_map_option.rs | 34 + .../methods/from_iter_instead_of_collect.rs | 11 +- clippy_lints/src/methods/implicit_clone.rs | 28 +- .../methods/manual_saturating_arithmetic.rs | 6 +- clippy_lints/src/methods/mod.rs | 101 +-- .../src/methods/option_map_or_none.rs | 7 +- .../src/methods/uninit_assumed_init.rs | 5 +- .../src/methods/unnecessary_filter_map.rs | 9 +- .../src/methods/wrong_self_convention.rs | 8 + clippy_lints/src/misc.rs | 22 +- clippy_lints/src/missing_const_for_fn.rs | 2 +- clippy_lints/src/modulo_arithmetic.rs | 2 +- clippy_lints/src/needless_bool.rs | 4 +- clippy_lints/src/needless_pass_by_value.rs | 2 +- clippy_lints/src/needless_question_mark.rs | 8 +- clippy_lints/src/option_if_let_else.rs | 6 +- clippy_lints/src/panic_in_result_fn.rs | 8 +- clippy_lints/src/panic_unimplemented.rs | 2 +- clippy_lints/src/pass_by_ref_or_value.rs | 8 + clippy_lints/src/ptr.rs | 94 ++- clippy_lints/src/question_mark.rs | 24 +- clippy_lints/src/returns.rs | 8 +- .../src/single_component_path_imports.rs | 125 +++- clippy_lints/src/size_of_in_element_count.rs | 4 +- .../src/slow_vector_initialization.rs | 69 +- .../src/suspicious_operation_groupings.rs | 2 +- clippy_lints/src/tabs_in_doc_comments.rs | 31 +- clippy_lints/src/to_string_in_display.rs | 4 +- clippy_lints/src/transmute/mod.rs | 2 +- clippy_lints/src/transmuting_null.rs | 21 +- clippy_lints/src/try_err.rs | 14 +- clippy_lints/src/types/borrowed_box.rs | 9 +- clippy_lints/src/unnecessary_self_imports.rs | 67 ++ clippy_lints/src/unnecessary_wraps.rs | 21 +- clippy_lints/src/unused_io_amount.rs | 2 +- clippy_lints/src/utils/internal_lints.rs | 14 +- clippy_lints/src/write.rs | 2 +- clippy_utils/src/ast_utils.rs | 32 + clippy_utils/src/attrs.rs | 3 +- clippy_utils/src/hir_utils.rs | 12 +- clippy_utils/src/lib.rs | 358 ++++++--- clippy_utils/src/paths.rs | 31 +- clippy_utils/src/qualify_min_const_fn.rs | 40 +- clippy_utils/src/source.rs | 9 + clippy_utils/src/ty.rs | 21 +- doc/adding_lints.md | 2 +- lintcheck/src/main.rs | 2 +- rust-toolchain | 2 +- .../collapsible_span_lint_calls.fixed | 48 +- .../collapsible_span_lint_calls.rs | 48 +- .../collapsible_span_lint_calls.stderr | 10 +- tests/ui-internal/match_type_on_diag_item.rs | 21 +- .../match_type_on_diag_item.stderr | 30 +- tests/ui/asm_syntax.rs | 5 +- tests/ui/asm_syntax.stderr | 10 +- tests/ui/bool_assert_comparison.rs | 59 ++ tests/ui/bool_assert_comparison.stderr | 112 +++ .../branches_sharing_code/shared_at_bottom.rs | 14 + .../ui/branches_sharing_code/shared_at_top.rs | 11 + tests/ui/cloned_instead_of_copied.fixed | 15 + tests/ui/cloned_instead_of_copied.rs | 15 + tests/ui/cloned_instead_of_copied.stderr | 34 + tests/ui/crashes/ice-5835.rs | 9 + tests/ui/crashes/ice-5835.stderr | 10 + tests/ui/deprecated.rs | 1 + tests/ui/deprecated.stderr | 36 +- tests/ui/entry.fixed | 155 ++++ tests/ui/entry.rs | 159 ++++ tests/ui/entry.stderr | 186 +++++ tests/ui/entry_fixable.fixed | 15 - tests/ui/entry_fixable.rs | 17 - tests/ui/entry_fixable.stderr | 12 - tests/ui/entry_unfixable.rs | 73 -- tests/ui/entry_unfixable.stderr | 57 -- tests/ui/entry_with_else.fixed | 73 ++ tests/ui/entry_with_else.rs | 60 ++ tests/ui/entry_with_else.stderr | 142 ++++ tests/ui/filter_methods.rs | 25 - tests/ui/filter_methods.stderr | 39 - tests/ui/flat_map_option.fixed | 13 + tests/ui/flat_map_option.rs | 13 + tests/ui/flat_map_option.stderr | 16 + tests/ui/for_loop_fixable.fixed | 26 + tests/ui/for_loop_fixable.rs | 26 + tests/ui/format.fixed | 4 + tests/ui/format.rs | 4 + tests/ui/format.stderr | 8 +- tests/ui/from_over_into.stderr | 2 +- .../ui/inconsistent_struct_constructor.fixed | 13 + tests/ui/inconsistent_struct_constructor.rs | 13 + .../ui/inconsistent_struct_constructor.stderr | 4 +- tests/ui/invalid_null_ptr_usage.fixed | 49 ++ tests/ui/invalid_null_ptr_usage.rs | 49 ++ tests/ui/invalid_null_ptr_usage.stderr | 154 ++++ tests/ui/macro_use_imports.fixed | 6 +- tests/ui/macro_use_imports.rs | 6 +- tests/ui/manual_map_option.fixed | 7 + tests/ui/manual_map_option.rs | 7 + .../missing_const_for_fn/auxiliary/helper.rs | 8 + .../ui/missing_const_for_fn/cant_be_const.rs | 19 + .../ui/missing_const_for_fn/could_be_const.rs | 10 + .../could_be_const.stderr | 26 +- tests/ui/needless_return.fixed | 88 ++- tests/ui/needless_return.rs | 88 ++- tests/ui/needless_return.stderr | 122 ++- .../ui/panic_in_result_fn_debug_assertions.rs | 23 +- ...panic_in_result_fn_debug_assertions.stderr | 57 -- tests/ui/panicking_macros.rs | 14 +- ...edundant_pattern_matching_drop_order.fixed | 58 ++ .../redundant_pattern_matching_drop_order.rs | 58 ++ ...dundant_pattern_matching_drop_order.stderr | 171 +++++ .../redundant_pattern_matching_option.fixed | 7 +- tests/ui/redundant_pattern_matching_option.rs | 7 +- .../redundant_pattern_matching_option.stderr | 38 +- .../ui/redundant_pattern_matching_poll.fixed | 7 +- tests/ui/redundant_pattern_matching_poll.rs | 7 +- .../ui/redundant_pattern_matching_poll.stderr | 36 +- .../redundant_pattern_matching_result.fixed | 3 +- tests/ui/redundant_pattern_matching_result.rs | 3 +- .../redundant_pattern_matching_result.stderr | 44 +- tests/ui/repl_uninit.rs | 4 +- tests/ui/single_component_path_imports.fixed | 13 + tests/ui/single_component_path_imports.rs | 13 + tests/ui/single_component_path_imports.stderr | 12 +- ...gle_component_path_imports_nested_first.rs | 17 + ...component_path_imports_nested_first.stderr | 25 + ...ingle_component_path_imports_self_after.rs | 16 + ...ngle_component_path_imports_self_before.rs | 17 + tests/ui/single_match.rs | 8 + tests/ui/single_match.stderr | 11 +- tests/ui/string_lit_as_bytes.fixed | 2 +- tests/ui/string_lit_as_bytes.rs | 2 +- tests/ui/string_lit_as_bytes.stderr | 4 +- tests/ui/suspicious_else_formatting.rs | 26 + tests/ui/suspicious_else_formatting.stderr | 39 +- tests/ui/try_err.fixed | 8 + tests/ui/try_err.rs | 8 + tests/ui/try_err.stderr | 8 +- tests/ui/uninit.rs | 5 +- tests/ui/uninit.stderr | 8 +- tests/ui/unnecessary_self_imports.fixed | 10 + tests/ui/unnecessary_self_imports.rs | 10 + tests/ui/unnecessary_self_imports.stderr | 23 + tests/ui/wrong_self_convention2.rs | 12 + util/gh-pages/index.html | 2 +- 186 files changed, 4649 insertions(+), 1551 deletions(-) create mode 100644 clippy_lints/src/bool_assert_comparison.rs create mode 100644 clippy_lints/src/methods/cloned_instead_of_copied.rs delete mode 100644 clippy_lints/src/methods/filter_flat_map.rs delete mode 100644 clippy_lints/src/methods/filter_map_flat_map.rs delete mode 100644 clippy_lints/src/methods/filter_map_map.rs create mode 100644 clippy_lints/src/methods/flat_map_option.rs create mode 100644 clippy_lints/src/unnecessary_self_imports.rs create mode 100644 tests/ui/bool_assert_comparison.rs create mode 100644 tests/ui/bool_assert_comparison.stderr create mode 100644 tests/ui/cloned_instead_of_copied.fixed create mode 100644 tests/ui/cloned_instead_of_copied.rs create mode 100644 tests/ui/cloned_instead_of_copied.stderr create mode 100644 tests/ui/crashes/ice-5835.rs create mode 100644 tests/ui/crashes/ice-5835.stderr create mode 100644 tests/ui/entry.fixed create mode 100644 tests/ui/entry.rs create mode 100644 tests/ui/entry.stderr delete mode 100644 tests/ui/entry_fixable.fixed delete mode 100644 tests/ui/entry_fixable.rs delete mode 100644 tests/ui/entry_fixable.stderr delete mode 100644 tests/ui/entry_unfixable.rs delete mode 100644 tests/ui/entry_unfixable.stderr create mode 100644 tests/ui/entry_with_else.fixed create mode 100644 tests/ui/entry_with_else.rs create mode 100644 tests/ui/entry_with_else.stderr delete mode 100644 tests/ui/filter_methods.rs delete mode 100644 tests/ui/filter_methods.stderr create mode 100644 tests/ui/flat_map_option.fixed create mode 100644 tests/ui/flat_map_option.rs create mode 100644 tests/ui/flat_map_option.stderr create mode 100644 tests/ui/invalid_null_ptr_usage.fixed create mode 100644 tests/ui/invalid_null_ptr_usage.rs create mode 100644 tests/ui/invalid_null_ptr_usage.stderr create mode 100644 tests/ui/missing_const_for_fn/auxiliary/helper.rs delete mode 100644 tests/ui/panic_in_result_fn_debug_assertions.stderr create mode 100644 tests/ui/redundant_pattern_matching_drop_order.fixed create mode 100644 tests/ui/redundant_pattern_matching_drop_order.rs create mode 100644 tests/ui/redundant_pattern_matching_drop_order.stderr create mode 100644 tests/ui/single_component_path_imports_nested_first.rs create mode 100644 tests/ui/single_component_path_imports_nested_first.stderr create mode 100644 tests/ui/single_component_path_imports_self_after.rs create mode 100644 tests/ui/single_component_path_imports_self_before.rs create mode 100644 tests/ui/unnecessary_self_imports.fixed create mode 100644 tests/ui/unnecessary_self_imports.rs create mode 100644 tests/ui/unnecessary_self_imports.stderr diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index a3f114e0bb34f..780ee9d63dfd2 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -3,7 +3,9 @@ Thank you for making Clippy better! We're collecting our changelog from pull request descriptions. If your PR only includes internal changes, you can just write `changelog: none`. Otherwise, please write a short comment -explaining your change. +explaining your change. Also, it's helpful for us that +the lint name is put into brackets `[]` and backticks `` ` ` ``, +e.g. ``[`lint_name`]``. If your PR fixes an issue, you can add "fixes #issue_number" into this PR description. This way the issue will be automatically closed when @@ -29,4 +31,5 @@ Delete this line and everything above before opening your PR. --- *Please write a short comment explaining your change (or "none" for internal only changes)* + changelog: diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 47253eecc4c4c..ae6f1aa1b30be 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -34,15 +34,16 @@ jobs: run: | MESSAGE=$(git log --format=%B -n 1) PR=$(echo "$MESSAGE" | grep -o "#[0-9]*" | head -1 | sed -e 's/^#//') - output=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$PR" | \ - python -c "import sys, json; print(json.load(sys.stdin)['body'])" | \ - grep "^changelog: " | \ - sed "s/changelog: //g") - if [[ -z "$output" ]]; then + body=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$PR" | \ + python -c "import sys, json; print(json.load(sys.stdin)['body'])") + output=$(grep "^changelog:\s*\S" <<< "$body" | sed "s/changelog:\s*//g") || { echo "ERROR: PR body must contain 'changelog: ...'" exit 1 - elif [[ "$output" = "none" ]]; then + } + if [[ "$output" = "none" ]]; then echo "WARNING: changelog is 'none'" + else + echo "changelog: $output" fi env: PYTHONIOENCODING: 'utf-8' diff --git a/CHANGELOG.md b/CHANGELOG.md index 73997192ae0d5..204d56e2a9854 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -179,7 +179,7 @@ Current stable, released 2021-03-25 * 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 +* `unknown_clippy_lints` Now integrated in the `unknown_lints` rustc lint [#6653](https://github.com/rust-lang/rust-clippy/pull/6653) ### Enhancements @@ -280,7 +280,7 @@ Released 2021-02-11 * 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 `panic_fmt` +* Deprecate `panic_params` lint. This is now available in rustc as `non_fmt_panic` [#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) @@ -419,7 +419,7 @@ Released 2020-12-31 [#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) +* 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) @@ -1018,7 +1018,7 @@ Released 2020-03-12 [#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) +* Deprecate `unused_label` [#4930](https://github.com/rust-lang/rust-clippy/pull/4930) ### Enhancements @@ -1046,7 +1046,7 @@ Released 2020-03-12 * [`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) +* `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) @@ -1080,7 +1080,7 @@ Released 2020-01-30 [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) +* 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) @@ -1282,7 +1282,7 @@ 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 +* 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 @@ -1375,7 +1375,7 @@ Released 2019-01-17 * New lints: [`slow_vector_initialization`], [`mem_discriminant_non_enum`], [`redundant_clone`], [`wildcard_dependencies`], - [`into_iter_on_ref`], [`into_iter_on_array`], [`deprecated_cfg_attr`], + [`into_iter_on_ref`], `into_iter_on_array`, [`deprecated_cfg_attr`], [`mem_discriminant_non_enum`], [`cargo_common_metadata`] * Add support for `u128` and `i128` to integer related lints * Add float support to `mistyped_literal_suffixes` @@ -1649,7 +1649,7 @@ Released 2018-09-13 ## 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`], +* 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`] @@ -2037,7 +2037,7 @@ Released 2018-09-13 ## 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`] +* 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)* @@ -2091,7 +2091,7 @@ Released 2018-09-13 ## 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`] +* New lints: [`overflow_check_conditional`], `unused_label`, [`new_without_default`] ## 0.0.48 — 2016-03-07 * Fixed: ICE in [`needless_range_loop`] with globals @@ -2124,6 +2124,7 @@ Released 2018-09-13 [`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 @@ -2148,6 +2149,7 @@ Released 2018-09-13 [`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 @@ -2178,7 +2180,6 @@ Released 2018-09-13 [`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use [`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg [`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens -[`drop_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_bounds [`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy [`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref [`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument @@ -2216,6 +2217,7 @@ Released 2018-09-13 [`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 @@ -2264,10 +2266,9 @@ Released 2018-09-13 [`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one [`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic [`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division -[`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array [`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref [`invalid_atomic_ordering`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_atomic_ordering -[`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref +[`invalid_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 @@ -2402,7 +2403,6 @@ Released 2018-09-13 [`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 -[`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params [`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap [`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl [`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite @@ -2488,7 +2488,6 @@ Released 2018-09-13 [`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting [`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment -[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr [`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some [`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display [`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo @@ -2517,13 +2516,13 @@ Released 2018-09-13 [`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg [`unit_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_cmp [`unit_return_expecting_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_return_expecting_ord -[`unknown_clippy_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#unknown_clippy_lints [`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast [`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map [`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold [`unnecessary_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 @@ -2541,7 +2540,6 @@ Released 2018-09-13 [`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice [`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect [`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount -[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit [`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e0a4d4455e9c6..02d2b63c9e199 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,10 +21,10 @@ All contributors are expected to follow the [Rust Code of Conduct]. - [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) + - [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) + - [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) diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index a0993bb6913e7..c565e29d07801 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -127,10 +127,9 @@ fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) _ => &block.expr, }; // function call - if let Some(args) = match_panic_call(cx, begin_panic_call); - if args.len() == 1; + if let Some(arg) = match_panic_call(cx, begin_panic_call); // bind the second argument of the `assert!` macro if it exists - if let panic_message = snippet_opt(cx, args[0].span); + if let panic_message = snippet_opt(cx, arg.span); // second argument of begin_panic is irrelevant // as is the second match arm then { diff --git a/clippy_lints/src/bool_assert_comparison.rs b/clippy_lints/src/bool_assert_comparison.rs new file mode 100644 index 0000000000000..bee706ed40215 --- /dev/null +++ b/clippy_lints/src/bool_assert_comparison.rs @@ -0,0 +1,75 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::{ast_utils, is_direct_expn_of}; +use rustc_ast::ast::{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:** This lint warns about boolean comparisons in assert-like macros. + /// + /// **Why is this bad?** It is shorter to use the equivalent. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // Bad + /// assert_eq!("a".is_empty(), false); + /// assert_ne!("a".is_empty(), true); + /// + /// // Good + /// assert!(!"a".is_empty()); + /// ``` + pub BOOL_ASSERT_COMPARISON, + style, + "Using a boolean as comparison value in an assert_* macro when there is no need" +} + +declare_lint_pass!(BoolAssertComparison => [BOOL_ASSERT_COMPARISON]); + +fn is_bool_lit(e: &Expr) -> bool { + matches!( + e.kind, + ExprKind::Lit(Lit { + kind: LitKind::Bool(_), + .. + }) + ) && !e.span.from_expansion() +} + +impl EarlyLintPass for BoolAssertComparison { + fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) { + let macros = ["assert_eq", "debug_assert_eq"]; + let inverted_macros = ["assert_ne", "debug_assert_ne"]; + + for mac in macros.iter().chain(inverted_macros.iter()) { + if let Some(span) = is_direct_expn_of(e.span, mac) { + if let Some([a, b]) = ast_utils::extract_assert_macro_args(e) { + let nb_bool_args = is_bool_lit(a) as usize + is_bool_lit(b) as usize; + + if nb_bool_args != 1 { + // If there are two boolean arguments, we definitely don't understand + // what's going on, so better leave things as is... + // + // Or there is simply no boolean and then we can leave things as is! + return; + } + + let non_eq_mac = &mac[..mac.len() - 3]; + span_lint_and_sugg( + cx, + BOOL_ASSERT_COMPARISON, + span, + &format!("used `{}!` with a literal bool", mac), + "replace it with", + format!("{}!(..)", non_eq_mac), + Applicability::MaybeIncorrect, + ); + return; + } + } + } + } +} diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 58d9aa9c005c2..67f0e0c78700b 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -261,7 +261,7 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { } METHODS_WITH_NEGATION .iter() - .cloned() + .copied() .flat_map(|(a, b)| vec![(a, b), (b, a)]) .find(|&(a, _)| { let path: &str = &path.ident.name.as_str(); diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index d7136f84cc3af..6a2666bc6c011 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -323,7 +323,7 @@ fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function: if let [int] = &*tp.segments; then { let name = &int.ident.name.as_str(); - candidates.iter().find(|c| name == *c).cloned() + candidates.iter().find(|c| name == *c).copied() } else { None } @@ -337,7 +337,7 @@ fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> { if let [ty] = &*path.segments; then { let name = &ty.ident.name.as_str(); - INTS.iter().find(|c| name == *c).cloned() + INTS.iter().find(|c| name == *c).copied() } else { None } diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/collapsible_match.rs index 04fff237bb4cb..ab22578abd674 100644 --- a/clippy_lints/src/collapsible_match.rs +++ b/clippy_lints/src/collapsible_match.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::visitors::LocalUsedVisitor; -use clippy_utils::{path_to_local, SpanlessEq}; +use clippy_utils::{is_lang_ctor, path_to_local, SpanlessEq}; use if_chain::if_chain; -use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; -use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, QPath, StmtKind, UnOp}; +use rustc_hir::LangItem::OptionNone; +use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{DefIdTree, TyCtxt, TypeckResults}; +use rustc_middle::ty::TypeckResults; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{MultiSpan, Span}; @@ -52,7 +52,7 @@ declare_lint_pass!(CollapsibleMatch => [COLLAPSIBLE_MATCH]); impl<'tcx> LateLintPass<'tcx> for CollapsibleMatch { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if let ExprKind::Match(_expr, arms, _source) = expr.kind { - if let Some(wild_arm) = arms.iter().rfind(|arm| arm_is_wild_like(arm, cx.tcx)) { + if let Some(wild_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) { for arm in arms { check_arm(arm, wild_arm, cx); } @@ -75,7 +75,7 @@ fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext // match { .. } if let Some(binding_id) = path_to_local(strip_ref_operators(expr_in, cx.typeck_results())); // one of the branches must be "wild-like" - if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(arm_inner, cx.tcx)); + if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(cx, arm_inner)); let (wild_inner_arm, non_wild_inner_arm) = (&arms_inner[wild_inner_arm_idx], &arms_inner[1 - wild_inner_arm_idx]); if !pat_contains_or(non_wild_inner_arm.pat); @@ -126,13 +126,13 @@ fn strip_singleton_blocks<'hir>(mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> /// A "wild-like" pattern is wild ("_") or `None`. /// For this lint to apply, both the outer and inner match expressions /// must have "wild-like" branches that can be combined. -fn arm_is_wild_like(arm: &Arm<'_>, tcx: TyCtxt<'_>) -> bool { +fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { if arm.guard.is_some() { return false; } match arm.pat.kind { PatKind::Binding(..) | PatKind::Wild => true, - PatKind::Path(QPath::Resolved(None, path)) if is_none_ctor(path.res, tcx) => true, + PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), _ => false, } } @@ -164,17 +164,6 @@ fn pat_contains_or(pat: &Pat<'_>) -> bool { result } -fn is_none_ctor(res: Res, tcx: TyCtxt<'_>) -> bool { - if let Some(none_id) = tcx.lang_items().option_none_variant() { - if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = res { - if let Some(variant_id) = tcx.parent(id) { - return variant_id == none_id; - } - } - } - false -} - /// 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. fn strip_ref_operators<'hir>(mut expr: &'hir Expr<'hir>, typeck_results: &TypeckResults<'_>) -> &'hir Expr<'hir> { diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index 31ae63b51849c..42e153909ce75 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::implements_trait; -use clippy_utils::{get_trait_def_id, if_sequence, parent_node_is_if_expr, paths, SpanlessEq}; +use clippy_utils::{get_trait_def_id, if_sequence, is_else_clause, paths, SpanlessEq}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { } // We only care about the top-most `if` in the chain - if parent_node_is_if_expr(expr, cx) { + if is_else_clause(cx.tcx, expr) { return; } diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 8b503c9a0306b..f956d171bfbe0 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then}; use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt}; use clippy_utils::{ - both, count_eq, eq_expr_value, get_enclosing_block, get_parent_expr, if_sequence, in_macro, parent_node_is_if_expr, + both, count_eq, eq_expr_value, get_enclosing_block, get_parent_expr, if_sequence, in_macro, is_else_clause, run_lints, search_same, ContainsName, SpanlessEq, SpanlessHash, }; use if_chain::if_chain; @@ -188,13 +188,18 @@ fn lint_same_then_else<'tcx>( expr: &'tcx Expr<'_>, ) { // We only lint ifs with multiple blocks - if blocks.len() < 2 || parent_node_is_if_expr(expr, cx) { + if blocks.len() < 2 || is_else_clause(cx.tcx, expr) { return; } // Check if each block has shared code let has_expr = blocks[0].expr.is_some(); - let (start_eq, mut end_eq, expr_eq) = scan_block_for_eq(cx, blocks); + + let (start_eq, mut end_eq, expr_eq) = if let Some(block_eq) = scan_block_for_eq(cx, blocks) { + (block_eq.start_eq, block_eq.end_eq, block_eq.expr_eq) + } else { + return; + }; // BRANCHES_SHARING_CODE prerequisites if has_conditional_else || (start_eq == 0 && end_eq == 0 && (has_expr && !expr_eq)) { @@ -290,7 +295,19 @@ fn lint_same_then_else<'tcx>( } } -fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> (usize, usize, bool) { +struct BlockEqual { + /// The amount statements that are equal from the start + start_eq: usize, + /// The amount statements that are equal from the end + end_eq: usize, + /// An indication if the block expressions are the same. This will also be true if both are + /// `None` + expr_eq: bool, +} + +/// This function can also trigger the `IF_SAME_THEN_ELSE` in which case it'll return `None` to +/// abort any further processing and avoid duplicate lint triggers. +fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> Option { let mut start_eq = usize::MAX; let mut end_eq = usize::MAX; let mut expr_eq = true; @@ -332,7 +349,7 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> (usize, "same as this", ); - return (0, 0, false); + return None; } } @@ -352,7 +369,11 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> (usize, end_eq = min_block_size - start_eq; } - (start_eq, end_eq, expr_eq) + Some(BlockEqual { + start_eq, + end_eq, + expr_eq, + }) } fn check_for_warn_of_moved_symbol( diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 89088c533ed50..4688b3d51050d 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -93,15 +93,6 @@ declare_deprecated_lint! { "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 the warn-by-default - /// `invalid_value` rustc lint. - pub INVALID_REF, - "superseded by rustc lint `invalid_value`" -} - declare_deprecated_lint! { /// **What it does:** Nothing. This lint has been deprecated. /// @@ -110,24 +101,6 @@ declare_deprecated_lint! { "`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:** This lint has been uplifted to rustc and is now called - /// `array_into_iter`. - pub INTO_ITER_ON_ARRAY, - "this lint has been uplifted to rustc and is now called `array_into_iter`" -} - -declare_deprecated_lint! { - /// **What it does:** Nothing. This lint has been deprecated. - /// - /// **Deprecation reason:** This lint has been uplifted to rustc and is now called - /// `unused_labels`. - pub UNUSED_LABEL, - "this lint has been uplifted to rustc and is now called `unused_labels`" -} - declare_deprecated_lint! { /// **What it does:** Nothing. This lint has been deprecated. /// @@ -147,44 +120,17 @@ declare_deprecated_lint! { declare_deprecated_lint! { /// **What it does:** Nothing. This lint has been deprecated. /// - /// **Deprecation reason:** This lint has been uplifted to rustc and is now called - /// `drop_bounds`. - pub DROP_BOUNDS, - "this lint has been uplifted to rustc and is now called `drop_bounds`" -} - -declare_deprecated_lint! { - /// **What it does:** Nothing. This lint has been deprecated. - /// - /// **Deprecation reason:** This lint has been uplifted to rustc and is now called - /// `temporary_cstring_as_ptr`. - pub TEMPORARY_CSTRING_AS_PTR, - "this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`" -} - -declare_deprecated_lint! { - /// **What it does:** Nothing. This lint has been deprecated. - /// - /// **Deprecation reason:** This lint has been uplifted to rustc and is now called - /// `panic_fmt`. - pub PANIC_PARAMS, - "this lint has been uplifted to rustc and is now called `panic_fmt`" -} - -declare_deprecated_lint! { - /// **What it does:** Nothing. This lint has been deprecated. - /// - /// **Deprecation reason:** This lint has been integrated into the `unknown_lints` - /// rustc lint. - pub UNKNOWN_CLIPPY_LINTS, - "this lint has been integrated into the `unknown_lints` rustc lint" + /// **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_find_map`, a + /// **Deprecation reason:** This lint has been replaced by `manual_filter_map`, a /// more specific lint. - pub FIND_MAP, - "this lint has been replaced by `manual_find_map`, a more specific lint" + pub FILTER_MAP, + "this lint has been replaced by `manual_filter_map`, a more specific lint" } diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index a815df1691a1c..8db5050a5ac30 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -1,17 +1,19 @@ -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; -use clippy_utils::ty::{is_type_diagnostic_item, match_type}; -use clippy_utils::SpanlessEq; -use clippy_utils::{get_item_name, paths}; -use if_chain::if_chain; +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 rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{BorrowKind, Expr, ExprKind, UnOp}; +use rustc_hir::{ + intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}, + Block, Expr, ExprKind, Guard, HirId, Local, Stmt, StmtKind, UnOp, +}; 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::sym; +use rustc_span::{Span, SyntaxContext, DUMMY_SP}; +use std::fmt::Write; declare_clippy_lint! { /// **What it does:** Checks for uses of `contains_key` + `insert` on `HashMap` @@ -19,15 +21,14 @@ declare_clippy_lint! { /// /// **Why is this bad?** Using `entry` is more efficient. /// - /// **Known problems:** Some false negatives, eg.: + /// **Known problems:** The suggestion may have type inference errors in some cases. e.g. /// ```rust - /// # use std::collections::HashMap; - /// # let mut map = HashMap::new(); - /// # let v = 1; - /// # let k = 1; - /// if !map.contains_key(&k) { - /// map.insert(k.clone(), v); - /// } + /// let mut map = std::collections::HashMap::new(); + /// let _ = if !map.contains_key(&0) { + /// map.insert(0, 0) + /// } else { + /// None + /// }; /// ``` /// /// **Example:** @@ -56,132 +57,584 @@ declare_clippy_lint! { 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<'_>) { - if let ExprKind::If(check, then_block, ref else_block) = expr.kind { - if let ExprKind::Unary(UnOp::Not, check) = check.kind { - if let Some((ty, map, key)) = check_cond(cx, check) { - // in case of `if !m.contains_key(&k) { m.insert(k, v); }` - // we can give a better error message - let sole_expr = { - else_block.is_none() - && if let ExprKind::Block(then_block, _) = then_block.kind { - (then_block.expr.is_some() as usize) + then_block.stmts.len() == 1 - } else { - true - } - // XXXManishearth we can also check for if/else blocks containing `None`. - }; - - let mut visitor = InsertVisitor { - cx, - span: expr.span, - ty, - map, - key, - sole_expr, - }; - - walk_expr(&mut visitor, then_block); + let (cond_expr, then_expr, else_expr) = match expr.kind { + ExprKind::If(c, t, e) => (c, t, e), + _ => 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 if let Some(else_block) = *else_block { - if let Some((ty, map, key)) = check_cond(cx, check) { - let mut visitor = InsertVisitor { - cx, - span: expr.span, - ty, - map, - key, - sole_expr: false, - }; - - walk_expr(&mut visitor, else_block); + } 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, + ); } } -fn check_cond<'a>(cx: &LateContext<'_>, check: &'a Expr<'a>) -> Option<(&'static str, &'a Expr<'a>, &'a Expr<'a>)> { - if_chain! { - if let ExprKind::MethodCall(path, _, params, _) = check.kind; - if params.len() >= 2; - if path.ident.name == sym!(contains_key); - if let ExprKind::AddrOf(BorrowKind::Ref, _, key) = params[1].kind; - then { - let map = ¶ms[0]; - let obj_ty = cx.typeck_results().expr_ty(map).peel_refs(); +#[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", + } + } +} - return if match_type(cx, obj_ty, &paths::BTREEMAP) { - Some(("BTreeMap", map, key)) - } - else if is_type_diagnostic_item(cx, obj_ty, sym::hashmap_type) { - Some(("HashMap", map, key)) - } - else { - None +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> { + 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 } +} - 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> { + if let Self::Insertion(i) = self { Some(i) } else { None } + } +} +#[derive(Clone, Copy)] +struct Insertion<'tcx> { + call: &'tcx Expr<'tcx>, + value: &'tcx Expr<'tcx>, } -struct InsertVisitor<'a, 'tcx, 'b> { - cx: &'a LateContext<'tcx>, - span: Span, - ty: &'static str, - map: &'b Expr<'b>, - key: &'b Expr<'b>, - sole_expr: bool, +/// 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>, + /// A stack of loops the visitor is currently in. + loops: Vec, } +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 + } -impl<'a, 'tcx, 'b> Visitor<'tcx> for InsertVisitor<'a, 'tcx, 'b> { - type Map = Map<'tcx>; + /// 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 { + 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(Local { init: Some(e), .. }) => { + self.allow_insert_closure &= !self.in_tail_pos; + self.in_tail_pos = false; + self.is_single_insert = false; + self.visit_expr(e); + }, + _ => { + 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_chain! { - if let ExprKind::MethodCall(path, _, params, _) = expr.kind; - if params.len() == 3; - if path.ident.name == sym!(insert); - if get_item_name(self.cx, self.map) == get_item_name(self.cx, ¶ms[0]); - if SpanlessEq::new(self.cx).eq_expr(self.key, ¶ms[1]); - if snippet_opt(self.cx, self.map.span) == snippet_opt(self.cx, params[0].span); - then { - span_lint_and_then(self.cx, MAP_ENTRY, self.span, - &format!("usage of `contains_key` followed by `insert` on a `{}`", self.ty), |diag| { - if self.sole_expr { - let mut app = Applicability::MachineApplicable; - let help = format!("{}.entry({}).or_insert({});", - snippet_with_applicability(self.cx, self.map.span, "map", &mut app), - snippet_with_applicability(self.cx, params[1].span, "..", &mut app), - snippet_with_applicability(self.cx, params[2].span, "..", &mut app)); - - diag.span_suggestion( - self.span, - "consider using", - help, - Applicability::MachineApplicable, // snippet - ); + 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 { + 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); } - else { - let help = format!("consider using `{}.entry({})`", - snippet(self.cx, self.map.span, "map"), - snippet(self.cx, params[1].span, "..")); - - diag.span_label( - self.span, - &help, - ); + 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); + // Sub expressions are no longer in the tail position. + self.is_single_insert = false; + self.in_tail_pos = false; + walk_expr(self, expr); + }, + }, } + } +} + +struct InsertSearchResults<'tcx> { + edits: Vec>, + allow_insert_closure: bool, + is_single_insert: bool, +} +impl InsertSearchResults<'tcx> { + fn as_single_insertion(&self) -> Option> { + self.is_single_insert.then(|| self.edits[0].as_insertion().unwrap()) + } - if !self.sole_expr { - walk_expr(self, expr); + 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 nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None + + 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> { + 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(), + }; + 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, + }) } diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 249ee27330bf9..4e2dbf005d51c 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{in_macro, match_path_ast}; +use clippy_utils::in_macro; use rustc_ast::ast::{AssocItemKind, Extern, FnKind, FnSig, ImplKind, Item, ItemKind, TraitKind, Ty, TyKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -126,7 +126,9 @@ impl_lint_pass!(ExcessiveBools => [STRUCT_EXCESSIVE_BOOLS, FN_PARAMS_EXCESSIVE_B fn is_bool_ty(ty: &Ty) -> bool { if let TyKind::Path(None, path) = &ty.kind { - return match_path_ast(path, &["bool"]); + if let [name] = path.segments.as_slice() { + return name.ident.name == sym::bool; + } } false } diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index 4729abbd8e3f7..c2b055ed6488e 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::paths; use clippy_utils::source::{snippet, snippet_opt}; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_expn_of, last_path_segment, match_def_path, match_function_call}; use if_chain::if_chain; @@ -100,15 +101,15 @@ fn on_argumentv1_new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: & return Some(format!("{:?}.to_string()", s.as_str())); } } else { - let snip = snippet(cx, format_args.span, ""); + let sugg = Sugg::hir(cx, format_args, ""); if let ExprKind::MethodCall(path, _, _, _) = format_args.kind { if path.ident.name == sym!(to_string) { - return Some(format!("{}", snip)); + return Some(format!("{}", sugg)); } } else if let ExprKind::Binary(..) = format_args.kind { - return Some(format!("{}", snip)); + return Some(format!("{}", sugg)); } - return Some(format!("{}.to_string()", snip)); + return Some(format!("{}.to_string()", sugg.maybe_par())); } } } @@ -136,7 +137,7 @@ fn on_new_v1<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option, expr: &Expr) { // the snippet should look like " else \n " with maybe comments anywhere // it’s bad when there is a ‘\n’ after the “else” if let Some(else_snippet) = snippet_opt(cx, else_span); - if let Some(else_pos) = else_snippet.find("else"); - if else_snippet[else_pos..].contains('\n'); + if let Some((pre_else, post_else)) = else_snippet.split_once("else"); + if let Some((_, post_else_post_eol)) = post_else.split_once('\n'); + then { + // Allow allman style braces `} \n else \n {` + if_chain! { + if is_block(else_); + if let Some((_, pre_else_post_eol)) = pre_else.split_once('\n'); + // Exactly one eol before and after the else + if !pre_else_post_eol.contains('\n'); + if !post_else_post_eol.contains('\n'); + then { + return; + } + } + let else_desc = if is_if(else_) { "if" } else { "{..}" }; span_lint_and_note( cx, diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index e5ec245e5029b..5e2baba894349 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -73,7 +73,7 @@ impl LateLintPass<'_> for FromOverInto { cx.tcx.sess.source_map().guess_head_span(item.span), "an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true", None, - "consider to implement `From` instead", + &format!("consider to implement `From<{}>` instead", impl_trait_ref.self_ty()), ); } } diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index ee16519692f9a..85c95f1151f84 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet_with_macro_callsite; -use clippy_utils::{match_qpath, meets_msrv, parent_node_is_if_expr}; +use clippy_utils::{is_else_clause, is_lang_ctor, meets_msrv}; use if_chain::if_chain; +use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -67,7 +68,7 @@ impl LateLintPass<'_> for IfThenSomeElseNone { } // We only care about the top-most `if` in the chain - if parent_node_is_if_expr(expr, cx) { + if is_else_clause(cx.tcx, expr) { return; } @@ -77,12 +78,12 @@ impl LateLintPass<'_> for IfThenSomeElseNone { if let Some(then_expr) = then_block.expr; if let ExprKind::Call(then_call, [then_arg]) = then_expr.kind; if let ExprKind::Path(ref then_call_qpath) = then_call.kind; - if match_qpath(then_call_qpath, &clippy_utils::paths::OPTION_SOME); + if is_lang_ctor(cx, then_call_qpath, OptionSome); if let ExprKind::Block(els_block, _) = els.kind; if els_block.stmts.is_empty(); if let Some(els_expr) = els_block.expr; - if let ExprKind::Path(ref els_call_qpath) = els_expr.kind; - if match_qpath(els_call_qpath, &clippy_utils::paths::OPTION_NONE); + if let ExprKind::Path(ref qpath) = els_expr.kind; + if is_lang_ctor(cx, qpath, OptionNone); then { let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]"); let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) { diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index 77a38544edc87..03fe0d16d480f 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -22,7 +22,7 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::paths; use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{differing_macro_contexts, match_path}; +use clippy_utils::{differing_macro_contexts, match_def_path}; declare_clippy_lint! { /// **What it does:** Checks for public `impl` or `fn` missing generalization @@ -333,12 +333,13 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't if let ExprKind::Call(fun, args) = e.kind; if let ExprKind::Path(QPath::TypeRelative(ty, method)) = fun.kind; if let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind; + if let Some(ty_did) = ty_path.res.opt_def_id(); then { if !TyS::same_type(self.target.ty(), self.maybe_typeck_results.unwrap().expr_ty(e)) { return; } - if match_path(ty_path, &paths::HASHMAP) { + if match_def_path(self.cx, ty_did, &paths::HASHMAP) { if method.ident.name == sym::new { self.suggestions .insert(e.span, "HashMap::default()".to_string()); @@ -351,7 +352,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't ), ); } - } else if match_path(ty_path, &paths::HASHSET) { + } else if match_def_path(self.cx, ty_did, &paths::HASHSET) { if method.ident.name == sym::new { self.suggestions .insert(e.span, "HashSet::default()".to_string()); diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index cba3183e86950..4069a685ea0a4 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{in_macro, match_qpath, SpanlessEq}; +use clippy_utils::{in_macro, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, StmtKind}; +use rustc_hir::{lang_items::LangItem, BinOpKind, Expr, ExprKind, QPath, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -87,7 +87,13 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { // Get the variable name let var_name = ares_path.segments[0].ident.name.as_str(); - const INT_TYPES: [&str; 5] = ["i8", "i16", "i32", "i64", "i128"]; + const INT_TYPES: [LangItem; 5] = [ + LangItem::I8, + LangItem::I16, + LangItem::I32, + LangItem::I64, + LangItem::Isize + ]; match cond_num_val.kind { ExprKind::Lit(ref cond_lit) => { @@ -99,17 +105,30 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { }; } }, - ExprKind::Path(ref cond_num_path) => { - if INT_TYPES.iter().any(|int_type| match_qpath(cond_num_path, &[int_type, "MIN"])) { - print_lint_and_sugg(cx, &var_name, expr); - }; + ExprKind::Path(QPath::TypeRelative(_, name)) => { + if_chain! { + if name.ident.as_str() == "MIN"; + if let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id); + if let Some(impl_id) = cx.tcx.impl_of_method(const_id); + let mut int_ids = INT_TYPES.iter().filter_map(|&ty| cx.tcx.lang_items().require(ty).ok()); + if int_ids.any(|int_id| int_id == impl_id); + then { + print_lint_and_sugg(cx, &var_name, expr) + } + } }, - ExprKind::Call(func, _) => { - if let ExprKind::Path(ref cond_num_path) = func.kind { - if INT_TYPES.iter().any(|int_type| match_qpath(cond_num_path, &[int_type, "min_value"])) { - print_lint_and_sugg(cx, &var_name, expr); + ExprKind::Call(func, []) => { + if_chain! { + if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind; + if name.ident.as_str() == "min_value"; + if let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id); + if let Some(impl_id) = cx.tcx.impl_of_method(func_id); + let mut int_ids = INT_TYPES.iter().filter_map(|&ty| cx.tcx.lang_items().require(ty).ok()); + if int_ids.any(|int_id| int_id == impl_id); + then { + print_lint_and_sugg(cx, &var_name, expr) } - }; + } }, _ => (), } diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs index d7ca24487a884..d138c3a8acfef 100644 --- a/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/clippy_lints/src/inconsistent_struct_constructor.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::in_macro; use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; @@ -66,6 +67,7 @@ declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRU impl LateLintPass<'_> for InconsistentStructConstructor { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if_chain! { + if !in_macro(expr.span); if let ExprKind::Struct(qpath, fields, base) = expr.kind; let ty = cx.typeck_results().expr_ty(expr); if let Some(adt_def) = ty.ty_adt_def(); diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index bbb4ddc613af5..afee20ce43e48 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::{implements_trait, match_type}; -use clippy_utils::{get_trait_def_id, higher, match_qpath, paths}; +use clippy_utils::{get_trait_def_id, higher, is_qpath_def_path, paths}; use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -163,7 +163,7 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) => is_infinite(cx, e), ExprKind::Call(path, _) => { if let ExprKind::Path(ref qpath) = path.kind { - match_qpath(qpath, &paths::REPEAT).into() + is_qpath_def_path(cx, qpath, path.hir_id, &paths::ITER_REPEAT).into() } else { Finite } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 11fef30945d78..d6134e178ca17 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -179,6 +179,7 @@ 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; @@ -357,6 +358,7 @@ mod unicode; mod unit_return_expecting_ord; mod unit_types; mod unnamed_address; +mod unnecessary_self_imports; mod unnecessary_sort_by; mod unnecessary_wraps; mod unnested_or_patterns; @@ -391,6 +393,7 @@ pub use crate::utils::conf::Conf; /// /// 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 write::Write::default()); store.register_pre_expansion_pass(|| box attrs::EarlyAttributes); store.register_pre_expansion_pass(|| box dbg_macro::DbgMacro); @@ -494,22 +497,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: "clippy::unsafe_vector_initialization", "the replacement suggested by this lint had substantially different behavior", ); - store.register_removed( - "clippy::invalid_ref", - "superseded by rustc lint `invalid_value`", - ); store.register_removed( "clippy::unused_collect", "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint", ); - store.register_removed( - "clippy::into_iter_on_array", - "this lint has been uplifted to rustc and is now called `array_into_iter`", - ); - store.register_removed( - "clippy::unused_label", - "this lint has been uplifted to rustc and is now called `unused_labels`", - ); store.register_removed( "clippy::replace_consts", "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants", @@ -518,26 +509,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: "clippy::regex_macro", "the regex! macro has been removed from the regex crate in 2018", ); - store.register_removed( - "clippy::drop_bounds", - "this lint has been uplifted to rustc and is now called `drop_bounds`", - ); - store.register_removed( - "clippy::temporary_cstring_as_ptr", - "this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`", - ); - store.register_removed( - "clippy::panic_params", - "this lint has been uplifted to rustc and is now called `panic_fmt`", - ); - store.register_removed( - "clippy::unknown_clippy_lints", - "this lint has been integrated into the `unknown_lints` rustc lint", - ); store.register_removed( "clippy::find_map", "this lint has been replaced by `manual_find_map`, a more specific lint", ); + store.register_removed( + "clippy::filter_map", + "this lint has been replaced by `manual_filter_map`, a more specific lint", + ); // end deprecated lints, do not remove this comment, it’s used in `update_lints` // begin register lints, do not remove this comment, it’s used in `update_lints` @@ -592,6 +571,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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, @@ -783,17 +763,18 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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::FILETYPE_IS_FILE, - methods::FILTER_MAP, 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, @@ -904,6 +885,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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, @@ -988,6 +970,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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, @@ -1073,6 +1056,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box default_numeric_fallback::DefaultNumericFallback); store.register_late_pass(|| box inconsistent_struct_constructor::InconsistentStructConstructor); store.register_late_pass(|| box non_octal_unix_permissions::NonOctalUnixPermissions); + store.register_early_pass(|| box unnecessary_self_imports::UnnecessarySelfImports); let msrv = conf.msrv.as_ref().and_then(|s| { parse_msrv(s, None, None).or_else(|| { @@ -1295,6 +1279,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box from_str_radix_10::FromStrRadix10); store.register_late_pass(|| box manual_map::ManualMap); store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv)); + store.register_early_pass(|| box bool_assert_comparison::BoolAssertComparison); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(arithmetic::FLOAT_ARITHMETIC), @@ -1345,6 +1330,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(strings::STRING_TO_STRING), LintId::of(strings::STR_TO_STRING), LintId::of(types::RC_BUFFER), + 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), @@ -1404,8 +1390,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS), LintId::of(matches::MATCH_WILD_ERR_ARM), LintId::of(matches::SINGLE_MATCH_ELSE), - LintId::of(methods::FILTER_MAP), + LintId::of(methods::CLONED_INSTEAD_OF_COPIED), LintId::of(methods::FILTER_MAP_NEXT), + LintId::of(methods::FLAT_MAP_OPTION), LintId::of(methods::IMPLICIT_CLONE), LintId::of(methods::INEFFICIENT_TO_STRING), LintId::of(methods::MAP_FLATTEN), @@ -1428,6 +1415,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(strings::STRING_ADD_ASSIGN), LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), + LintId::of(transmute::TRANSMUTE_PTR_TO_PTR), LintId::of(types::LINKEDLIST), LintId::of(types::OPTION_OPTION), LintId::of(unicode::NON_ASCII_LITERAL), @@ -1474,6 +1462,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1673,6 +1662,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1715,7 +1705,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(transmute::TRANSMUTE_INT_TO_BOOL), LintId::of(transmute::TRANSMUTE_INT_TO_CHAR), LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT), - LintId::of(transmute::TRANSMUTE_PTR_TO_PTR), LintId::of(transmute::TRANSMUTE_PTR_TO_REF), LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), LintId::of(transmute::WRONG_TRANSMUTE), @@ -1759,6 +1748,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), 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), @@ -1949,7 +1939,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(transmute::TRANSMUTE_INT_TO_BOOL), LintId::of(transmute::TRANSMUTE_INT_TO_CHAR), LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT), - LintId::of(transmute::TRANSMUTE_PTR_TO_PTR), LintId::of(transmute::TRANSMUTE_PTR_TO_REF), LintId::of(types::BORROWED_BOX), LintId::of(types::TYPE_COMPLEXITY), @@ -2012,6 +2001,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -2153,6 +2143,15 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { 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"); + + // 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_panic"); + ls.register_renamed("clippy::unknown_clippy_lints", "unknown_lints"); } // only exists to let the dogfood integration test works. diff --git a/clippy_lints/src/loops/explicit_into_iter_loop.rs b/clippy_lints/src/loops/explicit_into_iter_loop.rs index 4871a03118739..c7a28f42ea19b 100644 --- a/clippy_lints/src/loops/explicit_into_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_into_iter_loop.rs @@ -1,24 +1,25 @@ use super::EXPLICIT_INTO_ITER_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{match_trait_method, paths}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::TyS; -pub(super) fn check(cx: &LateContext<'_>, args: &'hir [Expr<'hir>], arg: &Expr<'_>) { - let receiver_ty = cx.typeck_results().expr_ty(&args[0]); - let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(&args[0]); - if !TyS::same_type(receiver_ty, receiver_ty_adjusted) { +pub(super) fn check(cx: &LateContext<'_>, self_arg: &'hir Expr<'hir>, call_expr: &Expr<'_>) { + let self_ty = cx.typeck_results().expr_ty(self_arg); + let self_ty_adjusted = cx.typeck_results().expr_ty_adjusted(self_arg); + if !(TyS::same_type(self_ty, self_ty_adjusted) && match_trait_method(cx, call_expr, &paths::INTO_ITERATOR)) { return; } let mut applicability = Applicability::MachineApplicable; - let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); + let object = snippet_with_applicability(cx, self_arg.span, "_", &mut applicability); span_lint_and_sugg( cx, EXPLICIT_INTO_ITER_LOOP, - arg.span, + call_expr.span, "it is more concise to loop over containers instead of using explicit \ iteration methods", "to write this more concisely, try", diff --git a/clippy_lints/src/loops/explicit_iter_loop.rs b/clippy_lints/src/loops/explicit_iter_loop.rs index 92aa2beb66d45..ce02ad013bef6 100644 --- a/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_iter_loop.rs @@ -9,12 +9,12 @@ use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty, TyS}; use rustc_span::sym; -pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], arg: &Expr<'_>, method_name: &str) { +pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, arg: &Expr<'_>, method_name: &str) { let should_lint = match method_name { - "iter" | "iter_mut" => is_ref_iterable_type(cx, &args[0]), + "iter" | "iter_mut" => is_ref_iterable_type(cx, self_arg), "into_iter" if match_trait_method(cx, arg, &paths::INTO_ITERATOR) => { - let receiver_ty = cx.typeck_results().expr_ty(&args[0]); - let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(&args[0]); + let receiver_ty = cx.typeck_results().expr_ty(self_arg); + let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(self_arg); let ref_receiver_ty = cx.tcx.mk_ref( cx.tcx.lifetimes.re_erased, ty::TypeAndMut { @@ -32,7 +32,7 @@ pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], arg: &Expr<'_>, met } let mut applicability = Applicability::MachineApplicable; - let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); + let object = snippet_with_applicability(cx, self_arg.span, "_", &mut applicability); let muta = if method_name == "iter_mut" { "mut " } else { "" }; span_lint_and_sugg( cx, diff --git a/clippy_lints/src/loops/manual_flatten.rs b/clippy_lints/src/loops/manual_flatten.rs index 94743cfcf4657..64ff7574f86b7 100644 --- a/clippy_lints/src/loops/manual_flatten.rs +++ b/clippy_lints/src/loops/manual_flatten.rs @@ -1,10 +1,11 @@ use super::utils::make_iterator_snippet; use super::MANUAL_FLATTEN; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{is_ok_ctor, is_some_ctor, path_to_local_id}; +use clippy_utils::{is_lang_ctor, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, StmtKind}; +use rustc_hir::LangItem::{OptionSome, ResultOk}; +use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, StmtKind}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::source_map::Span; @@ -42,9 +43,9 @@ pub(super) fn check<'tcx>( if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind; if path_to_local_id(match_expr, pat_hir_id); // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result` - if let PatKind::TupleStruct(QPath::Resolved(None, path), _, _) = match_arms[0].pat.kind; - let some_ctor = is_some_ctor(cx, path.res); - let ok_ctor = is_ok_ctor(cx, path.res); + if let PatKind::TupleStruct(ref qpath, _, _) = match_arms[0].pat.kind; + let some_ctor = is_lang_ctor(cx, qpath, OptionSome); + let ok_ctor = is_lang_ctor(cx, qpath, ResultOk); if some_ctor || ok_ctor; then { let if_let_type = if some_ctor { "Some" } else { "Ok" }; diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 28acefd51fef7..a4bc3e6bd100c 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -602,22 +602,19 @@ fn check_for_loop<'tcx>( fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>, expr: &Expr<'_>) { let mut next_loop_linted = false; // whether or not ITER_NEXT_LOOP lint was used - if let ExprKind::MethodCall(method, _, args, _) = arg.kind { - // just the receiver, no arguments - if args.len() == 1 { - let method_name = &*method.ident.as_str(); - // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x - match method_name { - "iter" | "iter_mut" => explicit_iter_loop::check(cx, args, arg, method_name), - "into_iter" => { - explicit_iter_loop::check(cx, args, arg, method_name); - explicit_into_iter_loop::check(cx, args, arg); - }, - "next" => { - next_loop_linted = iter_next_loop::check(cx, arg, expr); - }, - _ => {}, - } + if let ExprKind::MethodCall(method, _, [self_arg], _) = arg.kind { + let method_name = &*method.ident.as_str(); + // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x + match method_name { + "iter" | "iter_mut" => explicit_iter_loop::check(cx, self_arg, arg, method_name), + "into_iter" => { + explicit_iter_loop::check(cx, self_arg, arg, method_name); + explicit_into_iter_loop::check(cx, self_arg, arg); + }, + "next" => { + next_loop_linted = iter_next_loop::check(cx, arg, expr); + }, + _ => {}, } } diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 96720764e1658..e97b7c9417033 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -100,7 +100,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { ExprKind::Binary(_, e1, e2) | ExprKind::Assign(e1, e2, _) | ExprKind::AssignOp(_, e1, e2) - | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().cloned(), main_loop_id), + | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), main_loop_id), ExprKind::Loop(b, _, _, _) => { // Break can come from the inner loop so remove them. absorb_break(&never_loop_block(b, main_loop_id)) diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index c506d52e7462a..ec03daff87b07 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -112,6 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { let attrs = cx.tcx.hir().attrs(item.hir_id()); if let Some(mac_attr) = attrs.iter().find(|attr| attr.has_name(sym::macro_use)); if let Res::Def(DefKind::Mod, id) = path.res; + if !id.is_local(); then { for kid in cx.tcx.item_children(id).iter() { if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res { diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index 8c9e3af62f482..0b873534f2c8d 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -1,15 +1,14 @@ use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; -use clippy_utils::ty::{can_partially_move_ty, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; -use clippy_utils::{in_constant, is_allowed, is_else_clause, match_def_path, match_var, paths, peel_hir_expr_refs}; +use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; +use clippy_utils::{ + can_move_expr_to_closure, in_constant, is_allowed, is_else_clause, is_lang_ctor, match_var, peel_hir_expr_refs, +}; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::Applicability; -use rustc_hir::{ - def::Res, - intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}, - Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath, -}; +use rustc_hir::LangItem::{OptionNone, OptionSome}; +use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -103,12 +102,18 @@ impl LateLintPass<'_> for ManualMap { None => return, }; + // These two lints will go back and forth with each other. if cx.typeck_results().expr_ty(some_expr) == cx.tcx.types.unit && !is_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id) { return; } + // `map` won't perform any adjustments. + if !cx.typeck_results().expr_adjustments(some_expr).is_empty() { + return; + } + if !can_move_expr_to_closure(cx, some_expr) { return; } @@ -192,51 +197,6 @@ impl LateLintPass<'_> for ManualMap { } } -// Checks if the expression can be moved into a closure as is. -fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { - struct V<'cx, 'tcx> { - cx: &'cx LateContext<'tcx>, - make_closure: bool, - } - impl Visitor<'tcx> for V<'_, 'tcx> { - type Map = ErasedMap<'tcx>; - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - - fn visit_expr(&mut self, e: &'tcx Expr<'_>) { - match e.kind { - ExprKind::Break(..) - | ExprKind::Continue(_) - | ExprKind::Ret(_) - | ExprKind::Yield(..) - | ExprKind::InlineAsm(_) - | ExprKind::LlvmInlineAsm(_) => { - self.make_closure = false; - }, - // Accessing a field of a local value can only be done if the type isn't - // partially moved. - ExprKind::Field(base_expr, _) - if matches!( - base_expr.kind, - ExprKind::Path(QPath::Resolved(_, Path { res: Res::Local(_), .. })) - ) && can_partially_move_ty(self.cx, self.cx.typeck_results().expr_ty(base_expr)) => - { - // TODO: check if the local has been partially moved. Assume it has for now. - self.make_closure = false; - return; - } - _ => (), - }; - walk_expr(self, e); - } - } - - let mut v = V { cx, make_closure: true }; - v.visit_expr(expr); - v.make_closure -} - // Checks whether the expression could be passed as a function, or whether a closure is needed. // Returns the function to be passed to `map` if it exists. fn can_pass_as_func(cx: &LateContext<'tcx>, binding: Ident, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { @@ -269,20 +229,9 @@ fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxCon match pat.kind { PatKind::Wild => Some(OptionPat::Wild), PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), - PatKind::Path(QPath::Resolved(None, path)) - if path - .res - .opt_def_id() - .map_or(false, |id| match_def_path(cx, id, &paths::OPTION_NONE)) => - { - Some(OptionPat::None) - }, - PatKind::TupleStruct(QPath::Resolved(None, path), [pattern], _) - if path - .res - .opt_def_id() - .map_or(false, |id| match_def_path(cx, id, &paths::OPTION_SOME)) - && pat.span.ctxt() == ctxt => + PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone) => Some(OptionPat::None), + PatKind::TupleStruct(ref qpath, [pattern], _) + if is_lang_ctor(cx, qpath, OptionSome) && pat.span.ctxt() == ctxt => { Some(OptionPat::Some { pattern, ref_count }) }, @@ -298,17 +247,11 @@ fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ctxt: SyntaxConte match expr.kind { ExprKind::Call( Expr { - kind: ExprKind::Path(QPath::Resolved(None, path)), + kind: ExprKind::Path(ref qpath), .. }, [arg], - ) if ctxt == expr.span.ctxt() => { - if match_def_path(cx, path.res.opt_def_id()?, &paths::OPTION_SOME) { - Some(arg) - } else { - None - } - }, + ) if ctxt == expr.span.ctxt() && is_lang_ctor(cx, qpath, OptionSome) => Some(arg), ExprKind::Block( Block { stmts: [], @@ -324,10 +267,7 @@ fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ctxt: SyntaxConte // Checks for the `None` value. fn is_none_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { match expr.kind { - ExprKind::Path(QPath::Resolved(None, path)) => path - .res - .opt_def_id() - .map_or(false, |id| match_def_path(cx, id, &paths::OPTION_NONE)), + ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), ExprKind::Block( Block { stmts: [], diff --git a/clippy_lints/src/manual_ok_or.rs b/clippy_lints/src/manual_ok_or.rs index 9bfae602c407d..847c8c648b00a 100644 --- a/clippy_lints/src/manual_ok_or.rs +++ b/clippy_lints/src/manual_ok_or.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{match_qpath, path_to_local_id, paths}; +use clippy_utils::{is_lang_ctor, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; +use rustc_hir::LangItem::{ResultErr, ResultOk}; use rustc_hir::{Expr, ExprKind, PatKind}; use rustc_lint::LintContext; use rustc_lint::{LateContext, LateLintPass}; @@ -54,7 +55,7 @@ impl LateLintPass<'_> for ManualOkOr { let or_expr = &args[1]; if is_ok_wrapping(cx, &args[2]); if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind; - if match_qpath(err_path, &paths::RESULT_ERR); + if is_lang_ctor(cx, err_path, ResultErr); if let Some(method_receiver_snippet) = snippet_opt(cx, method_receiver.span); if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span); if let Some(indent) = indent_of(cx, scrutinee.span); @@ -81,7 +82,7 @@ impl LateLintPass<'_> for ManualOkOr { fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool { if let ExprKind::Path(ref qpath) = map_expr.kind { - if match_qpath(qpath, &paths::RESULT_OK) { + if is_lang_ctor(cx, qpath, ResultOk) { return true; } } @@ -90,7 +91,7 @@ fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool { let body = cx.tcx.hir().body(body_id); if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind; if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind; - if match_qpath(ok_path, &paths::RESULT_OK); + if is_lang_ctor(cx, ok_path, ResultOk); then { path_to_local_id(ok_arg, param_id) } else { false } } } diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index f296d6a1a15f5..65baa2552ccc6 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -3,10 +3,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::contains_return_break_continue_macro; -use clippy_utils::{in_constant, match_qpath, path_to_local_id, paths, sugg}; +use clippy_utils::{in_constant, is_lang_ctor, path_to_local_id, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Arm, Expr, ExprKind, Pat, PatKind}; +use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; +use rustc_hir::{Arm, Expr, ExprKind, PatKind}; use rustc_lint::LintContext; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; @@ -68,23 +69,21 @@ impl Case { } fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - fn applicable_or_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> { + fn applicable_or_arm<'a>(cx: &LateContext<'_>, arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> { if_chain! { if arms.len() == 2; if arms.iter().all(|arm| arm.guard.is_none()); - if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| + if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| { match arm.pat.kind { - PatKind::Path(ref some_qpath) => - match_qpath(some_qpath, &paths::OPTION_NONE), - PatKind::TupleStruct(ref err_qpath, &[Pat { kind: PatKind::Wild, .. }], _) => - match_qpath(err_qpath, &paths::RESULT_ERR), + PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), + PatKind::TupleStruct(ref qpath, &[pat], _) => + matches!(pat.kind, PatKind::Wild) && is_lang_ctor(cx, qpath, ResultErr), _ => false, } - ); + }); let unwrap_arm = &arms[1 - idx]; - if let PatKind::TupleStruct(ref unwrap_qpath, &[unwrap_pat], _) = unwrap_arm.pat.kind; - if match_qpath(unwrap_qpath, &paths::OPTION_SOME) - || match_qpath(unwrap_qpath, &paths::RESULT_OK); + if let PatKind::TupleStruct(ref qpath, &[unwrap_pat], _) = unwrap_arm.pat.kind; + if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk); if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind; if path_to_local_id(unwrap_arm.body, binding_hir_id); if !contains_return_break_continue_macro(or_arm.body); @@ -106,7 +105,7 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { } else { None }; - if let Some(or_arm) = applicable_or_arm(match_arms); + if let Some(or_arm) = applicable_or_arm(cx, match_arms); if let Some(or_body_snippet) = snippet_opt(cx, or_arm.body.span); if let Some(indent) = indent_of(cx, expr.span); if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some(); diff --git a/clippy_lints/src/map_identity.rs b/clippy_lints/src/map_identity.rs index e7719e7663d64..41cda23510ea2 100644 --- a/clippy_lints/src/map_identity.rs +++ b/clippy_lints/src/map_identity.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_adjusted, is_trait_method, match_path, match_var, paths, remove_blocks}; +use clippy_utils::{is_adjusted, is_qpath_def_path, is_trait_method, match_var, paths, remove_blocks}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Body, Expr, ExprKind, Pat, PatKind, QPath, StmtKind}; @@ -80,7 +80,7 @@ fn get_map_argument<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match expr.kind { ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)), - ExprKind::Path(QPath::Resolved(_, path)) => match_path(path, &paths::STD_CONVERT_IDENTITY), + ExprKind::Path(ref path) => is_qpath_def_path(cx, path, expr.hir_id, &paths::CONVERT_IDENTITY), _ => false, } } diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index c8ee0abd3dfef..44b4eb2903531 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -7,7 +7,7 @@ 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::LocalUsedVisitor; use clippy_utils::{ - get_parent_expr, in_macro, is_allowed, is_expn_of, is_refutable, is_wild, match_qpath, meets_msrv, path_to_local, + get_parent_expr, in_macro, is_allowed, is_expn_of, is_lang_ctor, is_refutable, is_wild, meets_msrv, 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}; @@ -15,6 +15,7 @@ use if_chain::if_chain; use rustc_ast::ast::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, @@ -422,7 +423,12 @@ declare_clippy_lint! { /// **Why is this bad?** It's more concise and clear to just use the proper /// utility function /// - /// **Known problems:** None. + /// **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:** /// @@ -737,8 +743,11 @@ fn report_single_match_single_pattern( 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(trait_id) = cx.tcx.lang_items().structural_peq_trait(); - if ty.is_integral() || ty.is_char() || ty.is_str() || implements_trait(cx, ty, trait_id, &[]); + 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 { @@ -1120,7 +1129,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) Applicability::MaybeIncorrect, ), variants => { - let mut suggestions: Vec<_> = variants.iter().cloned().map(format_suggestion).collect(); + let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect(); let message = if adt_def.is_variant_list_non_exhaustive() { suggestions.push("_".into()); "wildcard matches known variants and will also match future added variants" @@ -1189,10 +1198,10 @@ fn check_match_ref_pats(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], e 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 = if is_none_arm(&arms[0]) { - is_ref_some_arm(&arms[1]) - } else if is_none_arm(&arms[1]) { - is_ref_some_arm(&arms[0]) + let arm_ref: Option = 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 }; @@ -1500,7 +1509,7 @@ fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<' /// Gets all arms that are unbounded `PatRange`s. fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) -> Vec> { arms.iter() - .flat_map(|arm| { + .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 { @@ -1575,20 +1584,20 @@ fn is_unit_expr(expr: &Expr<'_>) -> bool { } // Checks if arm has the form `None => None` -fn is_none_arm(arm: &Arm<'_>) -> bool { - matches!(arm.pat.kind, PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_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(arm: &Arm<'_>) -> Option { +fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option { if_chain! { - if let PatKind::TupleStruct(ref path, pats, _) = arm.pat.kind; - if pats.len() == 1 && match_qpath(path, &paths::OPTION_SOME); + if let PatKind::TupleStruct(ref qpath, pats, _) = arm.pat.kind; + if is_lang_ctor(cx, qpath, OptionSome); if let PatKind::Binding(rb, .., ident, _) = pats[0].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 match_qpath(some_path, &paths::OPTION_SOME) && args.len() == 1; + 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 { @@ -1699,54 +1708,206 @@ where mod redundant_pattern_match { use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::span_lint_and_then; - use clippy_utils::source::snippet; - use clippy_utils::{is_trait_method, match_qpath, paths}; + 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_errors::Applicability; - use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind, QPath}; + 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, 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 ExprKind::Match(op, arms, ref match_source) = &expr.kind { match match_source { MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms), - MatchSource::IfLetDesugar { .. } => find_sugg_for_if_let(cx, expr, op, arms, "if"), - MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, arms, "while"), + MatchSource::IfLetDesugar { contains_else_clause } => { + find_sugg_for_if_let(cx, expr, op, &arms[0], "if", *contains_else_clause) + }, + MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, &arms[0], "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 { + 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(cx, ty)), + ty::Array(ty, _) => type_needs_ordered_drop(cx, ty), + ty::Adt(adt, subs) => adt + .all_fields() + .map(|f| f.ty(cx.tcx, subs)) + .any(|ty| type_needs_ordered_drop(cx, ty)), + _ => true, + } + } + // Check for std types which implement drop, but only for memory allocation. + else if is_type_diagnostic_item(cx, ty, sym::vec_type) + || is_type_lang_item(cx, ty, LangItem::OwnedBox) + || is_type_diagnostic_item(cx, ty, sym::Rc) + || is_type_diagnostic_item(cx, ty, sym::Arc) + || is_type_diagnostic_item(cx, ty, sym::cstring_type) + || match_type(cx, ty, &paths::BTREEMAP) + || match_type(cx, ty, &paths::LINKED_LIST) + || 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(cx, ty)) + } else { + true + } + } else { + true + } + } + + // Extract the generic arguments out of a type + fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option> { + 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 { + 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<'_>, - op: &Expr<'_>, - arms: &[Arm<'_>], + op: &'tcx Expr<'tcx>, + arm: &Arm<'_>, keyword: &'static str, + has_else: bool, ) { // also look inside refs - let mut kind = &arms[0].pat.kind; + let mut kind = &arm.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 good_method = match kind { - PatKind::TupleStruct(ref path, patterns, _) if patterns.len() == 1 => { - if let PatKind::Wild = patterns[0].kind { - if match_qpath(path, &paths::RESULT_OK) { - "is_ok()" - } else if match_qpath(path, &paths::RESULT_ERR) { - "is_err()" - } else if match_qpath(path, &paths::OPTION_SOME) { - "is_some()" - } else if match_qpath(path, &paths::POLL_READY) { - "is_ready()" - } else if match_qpath(path, &paths::IPADDR_V4) { - "is_ipv4()" - } else if match_qpath(path, &paths::IPADDR_V6) { - "is_ipv6()" + let op_ty = cx.typeck_results().expr_ty(op); + // 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; } @@ -1755,17 +1916,36 @@ mod redundant_pattern_match { } }, PatKind::Path(ref path) => { - if match_qpath(path, &paths::OPTION_NONE) { + let method = if is_lang_ctor(cx, path, OptionNone) { "is_none()" - } else if match_qpath(path, &paths::POLL_PENDING) { + } 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, op); + // check that `while_let_on_iterator` lint does not trigger if_chain! { if keyword == "while"; @@ -1784,7 +1964,7 @@ mod redundant_pattern_match { span_lint_and_then( cx, REDUNDANT_PATTERN_MATCHING, - arms[0].pat.span, + arm.pat.span, &format!("redundant pattern matching, consider using `{}`", good_method), |diag| { // while let ... = ... { ... } @@ -1798,12 +1978,20 @@ mod redundant_pattern_match { // while let ... = ... { ... } // ^^^^^^^^^^^^^^^^^^^ let span = expr_span.until(op_span.shrink_to_hi()); - diag.span_suggestion( - span, - "try this", - format!("{} {}.{}", keyword, snippet(cx, op_span, "_"), good_method), - Applicability::MachineApplicable, // snippet - ); + + 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"); + } }, ); } @@ -1819,6 +2007,7 @@ mod redundant_pattern_match { ) 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, @@ -1829,6 +2018,7 @@ mod redundant_pattern_match { ) .or_else(|| { find_good_method_for_match( + cx, arms, path_left, path_right, @@ -1848,6 +2038,7 @@ mod redundant_pattern_match { { if let PatKind::Wild = patterns[0].kind { find_good_method_for_match( + cx, arms, path_left, path_right, @@ -1858,6 +2049,7 @@ mod redundant_pattern_match { ) .or_else(|| { find_good_method_for_match( + cx, arms, path_left, path_right, @@ -1898,7 +2090,9 @@ mod redundant_pattern_match { } } + #[allow(clippy::too_many_arguments)] fn find_good_method_for_match<'a>( + cx: &LateContext<'_>, arms: &[Arm<'_>], path_left: &QPath<'_>, path_right: &QPath<'_>, @@ -1907,9 +2101,13 @@ mod redundant_pattern_match { should_be_left: &'a str, should_be_right: &'a str, ) -> Option<&'a str> { - let body_node_pair = if match_qpath(path_left, expected_left) && match_qpath(path_right, expected_right) { + 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 match_qpath(path_right, expected_left) && match_qpath(path_left, expected_right) { + } 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; diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index e1d351aee4547..ec60bffe95555 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::is_diagnostic_assoc_item; use clippy_utils::source::{snippet, snippet_with_applicability}; -use clippy_utils::{in_macro, match_def_path, match_qpath, meets_msrv, paths}; +use clippy_utils::{in_macro, is_diag_trait_item, is_lang_ctor, match_def_path, meets_msrv, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; +use rustc_hir::LangItem::OptionNone; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -102,7 +102,7 @@ impl_lint_pass!(MemReplace => fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) { if let ExprKind::Path(ref replacement_qpath) = src.kind { // Check that second argument is `Option::None` - if match_qpath(replacement_qpath, &paths::OPTION_NONE) { + if is_lang_ctor(cx, replacement_qpath, OptionNone) { // Since this is a late pass (already type-checked), // and we already know that the second argument is an // `Option`, we do not need to check the first @@ -210,17 +210,17 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath< sym::BinaryHeap, ]; - if std_types_symbols - .iter() - .any(|symbol| is_diagnostic_assoc_item(cx, def_id, *symbol)) - { - if let QPath::TypeRelative(_, method) = path { - if method.ident.name == sym::new { - return true; + 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 } @@ -230,7 +230,7 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr< if !in_external_macro(cx.tcx.sess, expr_span); 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_diagnostic_assoc_item(cx, repl_def_id, sym::Default) + if is_diag_trait_item(cx, repl_def_id, sym::Default) || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath); then { diff --git a/clippy_lints/src/methods/cloned_instead_of_copied.rs b/clippy_lints/src/methods/cloned_instead_of_copied.rs new file mode 100644 index 0000000000000..ba97ab3900ca4 --- /dev/null +++ b/clippy_lints/src/methods/cloned_instead_of_copied.rs @@ -0,0 +1,38 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_trait_method; +use clippy_utils::ty::{get_iterator_item_ty, is_copy}; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::{sym, Span}; + +use super::CLONED_INSTEAD_OF_COPIED; + +pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span) { + let recv_ty = cx.typeck_results().expr_ty_adjusted(recv); + let inner_ty = match recv_ty.kind() { + // `Option` -> `T` + ty::Adt(adt, subst) if cx.tcx.is_diagnostic_item(sym::option_type, adt.did) => subst.type_at(0), + _ if is_trait_method(cx, expr, sym::Iterator) => match get_iterator_item_ty(cx, recv_ty) { + // ::Item + Some(ty) => ty, + _ => return, + }, + _ => return, + }; + match inner_ty.kind() { + // &T where T: Copy + ty::Ref(_, ty, _) if is_copy(cx, ty) => {}, + _ => return, + }; + span_lint_and_sugg( + cx, + CLONED_INSTEAD_OF_COPIED, + span, + "used `cloned` where `copied` could be used instead", + "try", + "copied".into(), + Applicability::MachineApplicable, + ) +} diff --git a/clippy_lints/src/methods/filter_flat_map.rs b/clippy_lints/src/methods/filter_flat_map.rs deleted file mode 100644 index 1588eec88824b..0000000000000 --- a/clippy_lints/src/methods/filter_flat_map.rs +++ /dev/null @@ -1,18 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_trait_method; -use rustc_hir as hir; -use rustc_lint::LateContext; -use rustc_span::sym; - -use super::FILTER_MAP; - -/// lint use of `filter().flat_map()` for `Iterators` -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - // lint if caller of `.filter().flat_map()` is an Iterator - if is_trait_method(cx, expr, sym::Iterator) { - let msg = "called `filter(..).flat_map(..)` on an `Iterator`"; - let hint = "this is more succinctly expressed by calling `.flat_map(..)` \ - and filtering by returning `iter::empty()`"; - span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint); - } -} diff --git a/clippy_lints/src/methods/filter_map_flat_map.rs b/clippy_lints/src/methods/filter_map_flat_map.rs deleted file mode 100644 index 741b1e7e36125..0000000000000 --- a/clippy_lints/src/methods/filter_map_flat_map.rs +++ /dev/null @@ -1,18 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_trait_method; -use rustc_hir as hir; -use rustc_lint::LateContext; -use rustc_span::sym; - -use super::FILTER_MAP; - -/// lint use of `filter_map().flat_map()` for `Iterators` -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - // lint if caller of `.filter_map().flat_map()` is an Iterator - if is_trait_method(cx, expr, sym::Iterator) { - let msg = "called `filter_map(..).flat_map(..)` on an `Iterator`"; - let hint = "this is more succinctly expressed by calling `.flat_map(..)` \ - and filtering by returning `iter::empty()`"; - span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint); - } -} diff --git a/clippy_lints/src/methods/filter_map_identity.rs b/clippy_lints/src/methods/filter_map_identity.rs index 3a61f4ccad789..403fe8d354684 100644 --- a/clippy_lints/src/methods/filter_map_identity.rs +++ b/clippy_lints/src/methods/filter_map_identity.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_trait_method, match_qpath, path_to_local_id, paths}; +use clippy_utils::{is_expr_path_def_path, is_trait_method, path_to_local_id, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -33,14 +33,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_map_arg: } } - if_chain! { - if let hir::ExprKind::Path(ref qpath) = filter_map_arg.kind; - - if match_qpath(qpath, &paths::STD_CONVERT_IDENTITY); - - then { - apply_lint("called `filter_map(std::convert::identity)` on an `Iterator`"); - } + if is_expr_path_def_path(cx, filter_map_arg, &paths::CONVERT_IDENTITY) { + apply_lint("called `filter_map(std::convert::identity)` on an `Iterator`"); } } } diff --git a/clippy_lints/src/methods/filter_map_map.rs b/clippy_lints/src/methods/filter_map_map.rs deleted file mode 100644 index 713bbf258370d..0000000000000 --- a/clippy_lints/src/methods/filter_map_map.rs +++ /dev/null @@ -1,17 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_trait_method; -use rustc_hir as hir; -use rustc_lint::LateContext; -use rustc_span::sym; - -use super::FILTER_MAP; - -/// lint use of `filter_map().map()` for `Iterators` -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - // lint if caller of `.filter_map().map()` is an Iterator - if is_trait_method(cx, expr, sym::Iterator) { - let msg = "called `filter_map(..).map(..)` on an `Iterator`"; - let hint = "this is more succinctly expressed by only calling `.filter_map(..)` instead"; - span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint); - } -} diff --git a/clippy_lints/src/methods/flat_map_identity.rs b/clippy_lints/src/methods/flat_map_identity.rs index dd613d0cd6384..25f8434cb9442 100644 --- a/clippy_lints/src/methods/flat_map_identity.rs +++ b/clippy_lints/src/methods/flat_map_identity.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_trait_method, match_qpath, paths}; +use clippy_utils::{is_expr_path_def_path, is_trait_method, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -16,8 +16,6 @@ pub(super) fn check<'tcx>( flat_map_span: Span, ) { if is_trait_method(cx, expr, sym::Iterator) { - let arg_node = &flat_map_arg.kind; - let apply_lint = |message: &str| { span_lint_and_sugg( cx, @@ -31,8 +29,8 @@ pub(super) fn check<'tcx>( }; if_chain! { - if let hir::ExprKind::Closure(_, _, body_id, _, _) = arg_node; - let body = cx.tcx.hir().body(*body_id); + if let hir::ExprKind::Closure(_, _, body_id, _, _) = flat_map_arg.kind; + let body = cx.tcx.hir().body(body_id); if let hir::PatKind::Binding(_, _, binding_ident, _) = body.params[0].pat.kind; if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = body.value.kind; @@ -45,14 +43,8 @@ pub(super) fn check<'tcx>( } } - if_chain! { - if let hir::ExprKind::Path(ref qpath) = arg_node; - - if match_qpath(qpath, &paths::STD_CONVERT_IDENTITY); - - then { - apply_lint("called `flat_map(std::convert::identity)` on an `Iterator`"); - } + if is_expr_path_def_path(cx, flat_map_arg, &paths::CONVERT_IDENTITY) { + apply_lint("called `flat_map(std::convert::identity)` on an `Iterator`"); } } } diff --git a/clippy_lints/src/methods/flat_map_option.rs b/clippy_lints/src/methods/flat_map_option.rs new file mode 100644 index 0000000000000..12d560653edf3 --- /dev/null +++ b/clippy_lints/src/methods/flat_map_option.rs @@ -0,0 +1,34 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_trait_method; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::{source_map::Span, sym}; + +use super::FLAT_MAP_OPTION; +use clippy_utils::ty::is_type_diagnostic_item; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, span: Span) { + if !is_trait_method(cx, expr, sym::Iterator) { + return; + } + let arg_ty = cx.typeck_results().expr_ty_adjusted(arg); + let sig = match arg_ty.kind() { + ty::Closure(_, substs) => substs.as_closure().sig(), + _ if arg_ty.is_fn() => arg_ty.fn_sig(cx.tcx), + _ => return, + }; + if !is_type_diagnostic_item(cx, sig.output().skip_binder(), sym::option_type) { + return; + } + span_lint_and_sugg( + cx, + FLAT_MAP_OPTION, + span, + "used `flat_map` where `filter_map` could be used instead", + "try", + "filter_map".into(), + Applicability::MachineApplicable, + ) +} diff --git a/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/clippy_lints/src/methods/from_iter_instead_of_collect.rs index 707c54f7a3cad..28d0e8cd4ae9e 100644 --- a/clippy_lints/src/methods/from_iter_instead_of_collect.rs +++ b/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -1,26 +1,23 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::implements_trait; -use clippy_utils::{get_trait_def_id, match_qpath, paths, sugg}; +use clippy_utils::{is_expr_path_def_path, paths, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::ExprKind; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::Ty; use rustc_span::sym; use super::FROM_ITER_INSTEAD_OF_COLLECT; -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>], func_kind: &ExprKind<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>], func: &hir::Expr<'_>) { if_chain! { - if let hir::ExprKind::Path(path) = func_kind; - if match_qpath(path, &["from_iter"]); + if is_expr_path_def_path(cx, func, &paths::FROM_ITERATOR_METHOD); let ty = cx.typeck_results().expr_ty(expr); let arg_ty = cx.typeck_results().expr_ty(&args[0]); - if let Some(from_iter_id) = get_trait_def_id(cx, &paths::FROM_ITERATOR); if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator); - if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]); + if implements_trait(cx, arg_ty, iter_id, &[]); then { // `expr` implements `FromIterator` trait let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_par(); diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs index 1211e2f2bf7cb..81c42de145f6c 100644 --- a/clippy_lints/src/methods/implicit_clone.rs +++ b/clippy_lints/src/methods/implicit_clone.rs @@ -1,28 +1,36 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::{is_diag_item_method, is_diag_trait_item}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::ExprKind; use rustc_lint::LateContext; use rustc_middle::ty::TyS; -use rustc_span::symbol::Symbol; +use rustc_span::{sym, Span}; use super::IMPLICIT_CLONE; -use clippy_utils::is_diagnostic_assoc_item; -pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, trait_diagnostic: Symbol) { +pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, span: Span) { if_chain! { - if let ExprKind::MethodCall(method_path, _, [arg], _) = &expr.kind; + if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if match method_name { + "to_os_string" => is_diag_item_method(cx, method_def_id, sym::OsStr), + "to_owned" => is_diag_trait_item(cx, method_def_id, sym::ToOwned), + "to_path_buf" => is_diag_item_method(cx, method_def_id, sym::Path), + "to_vec" => cx.tcx.impl_of_method(method_def_id) + .map(|impl_did| Some(impl_did) == cx.tcx.lang_items().slice_alloc_impl()) + == Some(true), + _ => false, + }; let return_type = cx.typeck_results().expr_ty(expr); - let input_type = cx.typeck_results().expr_ty(arg).peel_refs(); - if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + let input_type = cx.typeck_results().expr_ty(recv).peel_refs(); if let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did)); if TyS::same_type(return_type, input_type); - if is_diagnostic_assoc_item(cx, expr_def_id, trait_diagnostic); then { span_lint_and_sugg( - cx,IMPLICIT_CLONE,method_path.ident.span, - &format!("implicitly cloning a `{}` by calling `{}` on its dereferenced type", ty_name, method_path.ident.name), + cx, + IMPLICIT_CLONE, + span, + &format!("implicitly cloning a `{}` by calling `{}` on its dereferenced type", ty_name, method_name), "consider using", "clone".to_string(), Applicability::MachineApplicable diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index ecb8b72ef4610..2fddea7068d96 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::match_qpath; +use clippy_utils::is_qpath_def_path; use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_ast::ast; @@ -94,11 +94,11 @@ fn is_min_or_max<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) -> Option = ["1", "2", "whee!"].iter().flat_map(|x| x.parse().ok()).collect(); + /// ``` + /// Use instead: + /// ```rust + /// let nums: Vec = ["1", "2", "whee!"].iter().filter_map(|x| x.parse().ok()).collect(); + /// ``` + pub FLAT_MAP_OPTION, + pedantic, + "used `flat_map` where `filter_map` could be used instead" +} + declare_clippy_lint! { /// **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s. /// @@ -472,35 +517,6 @@ declare_clippy_lint! { "using combinations of `flatten` and `map` which can usually be written as a single method call" } -declare_clippy_lint! { - /// **What it does:** Checks for usage of `_.filter(_).map(_)`, - /// `_.filter(_).flat_map(_)`, `_.filter_map(_).flat_map(_)` and similar. - /// - /// **Why is this bad?** Readability, this can be written more concisely as - /// `_.filter_map(_)`. - /// - /// **Known problems:** Often requires a condition + Option/Iterator creation - /// inside the closure. - /// - /// **Example:** - /// ```rust - /// let vec = vec![1]; - /// - /// // Bad - /// vec.iter().filter(|x| **x == 0).map(|x| *x * 2); - /// - /// // Good - /// vec.iter().filter_map(|x| if *x == 0 { - /// Some(*x * 2) - /// } else { - /// None - /// }); - /// ``` - pub FILTER_MAP, - pedantic, - "using combinations of `filter`, `map`, `filter_map` and `flat_map` which can usually be written as a single method call" -} - declare_clippy_lint! { /// **What it does:** Checks for usage of `_.filter(_).map(_)` that can be written more simply /// as `filter_map(_)`. @@ -1670,6 +1686,8 @@ impl_lint_pass!(Methods => [ CLONE_ON_COPY, CLONE_ON_REF_PTR, CLONE_DOUBLE_REF, + CLONED_INSTEAD_OF_COPIED, + FLAT_MAP_OPTION, INEFFICIENT_TO_STRING, NEW_RET_NO_SELF, SINGLE_CHAR_PATTERN, @@ -1677,7 +1695,6 @@ impl_lint_pass!(Methods => [ SEARCH_IS_SOME, FILTER_NEXT, SKIP_WHILE_NEXT, - FILTER_MAP, FILTER_MAP_IDENTITY, MANUAL_FILTER_MAP, MANUAL_FIND_MAP, @@ -1741,7 +1758,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { match expr.kind { hir::ExprKind::Call(func, args) => { - from_iter_instead_of_collect::check(cx, expr, args, &func.kind); + from_iter_instead_of_collect::check(cx, expr, args, func); }, hir::ExprKind::MethodCall(method_call, ref method_span, args, _) => { or_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args); @@ -1942,6 +1959,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv), ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), + ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span), ("collect", []) => match method_call!(recv) { Some(("cloned", [recv2], _)) => iter_cloned_collect::check(cx, expr, recv2), Some(("map", [m_recv, m_arg], _)) => { @@ -1965,10 +1983,9 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio unnecessary_filter_map::check(cx, expr, arg); filter_map_identity::check(cx, expr, arg, span); }, - ("flat_map", [flm_arg]) => match method_call!(recv) { - Some(("filter", [_, _], _)) => filter_flat_map::check(cx, expr), - Some(("filter_map", [_, _], _)) => filter_map_flat_map::check(cx, expr), - _ => flat_map_identity::check(cx, expr, flm_arg, span), + ("flat_map", [arg]) => { + flat_map_identity::check(cx, expr, arg, span); + flat_map_option::check(cx, expr, arg, span); }, ("flatten", []) => { if let Some(("map", [recv, map_arg], _)) = method_call!(recv) { @@ -1993,7 +2010,6 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio ("filter", [f_arg]) => { filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false) }, - ("filter_map", [_]) => filter_map_map::check(cx, expr), ("find", [f_arg]) => filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true), _ => {}, } @@ -2025,10 +2041,9 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio } }, ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg), - ("to_os_string", []) => implicit_clone::check(cx, expr, sym::OsStr), - ("to_owned", []) => implicit_clone::check(cx, expr, sym::ToOwned), - ("to_path_buf", []) => implicit_clone::check(cx, expr, sym::Path), - ("to_vec", []) => implicit_clone::check(cx, expr, sym::slice), + ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => { + implicit_clone::check(cx, name, expr, recv, span); + }, ("unwrap", []) => match method_call!(recv) { Some(("get", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, false), Some(("get_mut", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, true), diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs index 013a6f90ac97b..36a1c13d5be1e 100644 --- a/clippy_lints/src/methods/option_map_or_none.rs +++ b/clippy_lints/src/methods/option_map_or_none.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_lang_ctor; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{match_qpath, paths}; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_lint::LateContext; use rustc_span::symbol::sym; @@ -32,7 +33,7 @@ pub(super) fn check<'tcx>( let (lint_name, msg, instead, hint) = { let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = def_arg.kind { - match_qpath(qpath, &paths::OPTION_NONE) + is_lang_ctor(cx, qpath, OptionNone) } else { return; }; @@ -43,7 +44,7 @@ pub(super) fn check<'tcx>( } let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_arg.kind { - match_qpath(qpath, &paths::OPTION_SOME) + is_lang_ctor(cx, qpath, OptionSome) } else { false }; diff --git a/clippy_lints/src/methods/uninit_assumed_init.rs b/clippy_lints/src/methods/uninit_assumed_init.rs index 0ae65c0c01dba..1a5894e48d14c 100644 --- a/clippy_lints/src/methods/uninit_assumed_init.rs +++ b/clippy_lints/src/methods/uninit_assumed_init.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::{match_def_path, match_qpath, paths}; +use clippy_utils::{is_expr_path_def_path, match_def_path, paths}; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; @@ -12,8 +12,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr if_chain! { if let hir::ExprKind::Call(callee, args) = recv.kind; if args.is_empty(); - if let hir::ExprKind::Path(ref path) = callee.kind; - if match_qpath(path, &paths::MEM_MAYBEUNINIT_UNINIT); + if is_expr_path_def_path(cx, callee, &paths::MEM_MAYBEUNINIT_UNINIT); if !is_maybe_uninit_ty_valid(cx, cx.typeck_results().expr_ty_adjusted(expr)); then { span_lint( diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index 0f28bfdf09e89..b61c4ffe9b3ae 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::usage::mutated_variables; -use clippy_utils::{is_trait_method, match_qpath, path_to_local_id, paths}; +use clippy_utils::{is_lang_ctor, is_trait_method, path_to_local_id}; use rustc_hir as hir; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; +use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; use rustc_span::sym; @@ -54,14 +55,12 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc match &expr.kind { hir::ExprKind::Call(func, args) => { if let hir::ExprKind::Path(ref path) = func.kind { - if match_qpath(path, &paths::OPTION_SOME) { + if is_lang_ctor(cx, path, OptionSome) { if path_to_local_id(&args[0], arg_id) { return (false, false); } return (true, false); } - // We don't know. It might do anything. - return (true, true); } (true, true) }, @@ -85,7 +84,7 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc let else_check = check_expression(cx, arg_id, else_arm); (if_check.0 | else_check.0, if_check.1 | else_check.1) }, - hir::ExprKind::Path(path) if match_qpath(path, &paths::OPTION_NONE) => (false, true), + hir::ExprKind::Path(path) if is_lang_ctor(cx, path, OptionNone) => (false, true), _ => (true, true), } } diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index 1e0de249a91f1..6e2bcb113c2c1 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -102,6 +102,14 @@ pub(super) fn check<'tcx>( .iter() .all(|conv| conv.check(cx, self_ty, item_name, implements_trait, is_trait_item)) }) { + // don't lint if it implements a trait but not willing to check `Copy` types conventions (see #7032) + if implements_trait + && !conventions + .iter() + .any(|conv| matches!(conv, Convention::IsSelfTypeCopy(_))) + { + return; + } if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) { let suggestion = { if conventions.len() > 1 { diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index afced5a5ce58a..0b0cd9be46cf4 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -20,8 +20,8 @@ use rustc_span::symbol::sym; use crate::consts::{constant, Constant}; use clippy_utils::sugg::Sugg; use clippy_utils::{ - get_item_name, get_parent_expr, higher, in_constant, is_diagnostic_assoc_item, is_integer_const, iter_input_pats, - last_path_segment, match_qpath, unsext, SpanlessEq, + expr_path_res, get_item_name, get_parent_expr, higher, in_constant, is_diag_trait_item, is_integer_const, + iter_input_pats, last_path_segment, match_any_def_paths, paths, unsext, SpanlessEq, }; declare_clippy_lint! { @@ -555,8 +555,8 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: ExprKind::MethodCall(.., args, _) if args.len() == 1 => { if_chain!( if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if is_diagnostic_assoc_item(cx, expr_def_id, sym::ToString) - || is_diagnostic_assoc_item(cx, expr_def_id, sym::ToOwned); + if is_diag_trait_item(cx, expr_def_id, sym::ToString) + || is_diag_trait_item(cx, expr_def_id, sym::ToOwned); then { (cx.typeck_results().expr_ty(&args[0]), snippet(cx, args[0].span, "..")) } else { @@ -564,13 +564,13 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: } ) }, - ExprKind::Call(path, v) if v.len() == 1 => { - if let ExprKind::Path(ref path) = path.kind { - if match_qpath(path, &["String", "from_str"]) || match_qpath(path, &["String", "from"]) { - (cx.typeck_results().expr_ty(&v[0]), snippet(cx, v[0].span, "..")) - } else { - return; - } + ExprKind::Call(path, [arg]) => { + if expr_path_res(cx, path) + .opt_def_id() + .and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM])) + .is_some() + { + (cx.typeck_results().expr_ty(arg), snippet(cx, arg.span, "..")) } else { return; } diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 0dc02431ad538..93b7a897405ae 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -138,7 +138,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { let mir = cx.tcx.optimized_mir(def_id); - if let Err((span, err)) = is_min_const_fn(cx.tcx, mir) { + if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv.as_ref()) { if rustc_mir::const_eval::is_min_const_fn(cx.tcx, def_id.to_def_id()) { cx.tcx.sess.span_err(span, &err); } diff --git a/clippy_lints/src/modulo_arithmetic.rs b/clippy_lints/src/modulo_arithmetic.rs index 6a52de4f71364..64e9dc85466eb 100644 --- a/clippy_lints/src/modulo_arithmetic.rs +++ b/clippy_lints/src/modulo_arithmetic.rs @@ -4,7 +4,7 @@ use clippy_utils::sext; use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self}; +use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use std::fmt::Display; diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index 96a58d1410f22..dd4581986377f 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -5,7 +5,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; -use clippy_utils::{is_expn_of, parent_node_is_if_expr}; +use clippy_utils::{is_else_clause, is_expn_of}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp}; @@ -81,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { snip = snip.make_return(); } - if parent_node_is_if_expr(e, cx) { + if is_else_clause(cx.tcx, e) { snip = snip.blockify() } diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 780e224129347..e33a33e238633 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -279,7 +279,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { spans.extend( deref_span .iter() - .cloned() + .copied() .map(|span| (span, format!("*{}", snippet(cx, span, "")))), ); spans.sort_by_key(|&(span, _)| span); diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index 9852633b7342e..cfe7ae6630e04 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -1,9 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_lang_ctor; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{differing_macro_contexts, is_ok_ctor, is_some_ctor, meets_msrv}; +use clippy_utils::{differing_macro_contexts, meets_msrv}; use if_chain::if_chain; use rustc_errors::Applicability; +use rustc_hir::LangItem::{OptionSome, ResultOk}; use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_semver::RustcVersion; @@ -159,8 +161,8 @@ fn is_some_or_ok_call<'a>( if_chain! { // Check outer expression matches CALL_IDENT(ARGUMENT) format if let ExprKind::Call(path, args) = &expr.kind; - if let ExprKind::Path(QPath::Resolved(None, path)) = &path.kind; - if is_some_ctor(cx, path.res) || is_ok_ctor(cx, path.res); + if let ExprKind::Path(ref qpath) = &path.kind; + if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk); // Extract inner expression from ARGUMENT if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &args[0].kind; diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 1b9120ae45f54..e527adbb8929d 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::paths; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::contains_return_break_continue_macro; -use clippy_utils::{eager_or_lazy, get_enclosing_block, in_macro, match_qpath}; +use clippy_utils::{eager_or_lazy, get_enclosing_block, in_macro, is_lang_ctor}; use if_chain::if_chain; use rustc_errors::Applicability; +use rustc_hir::LangItem::OptionSome; use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -164,7 +164,7 @@ fn detect_option_if_let_else<'tcx>( if arms.len() == 2; if !is_result_ok(cx, cond_expr); // Don't lint on Result::ok because a different lint does it already if let PatKind::TupleStruct(struct_qpath, &[inner_pat], _) = &arms[0].pat.kind; - if match_qpath(struct_qpath, &paths::OPTION_SOME); + if is_lang_ctor(cx, struct_qpath, OptionSome); if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind; if !contains_return_break_continue_macro(arms[0].body); if !contains_return_break_continue_macro(arms[1].body); diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index d32b937b209c5..cef74d87e7c01 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{find_macro_calls, return_ty}; +use clippy_utils::{find_macro_calls, is_expn_of, return_ty}; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_lint::{LateContext, LateLintPass}; @@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { } fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) { - let panics = find_macro_calls( + let mut panics = find_macro_calls( &[ "unimplemented", "unreachable", @@ -61,12 +61,10 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir "assert", "assert_eq", "assert_ne", - "debug_assert", - "debug_assert_eq", - "debug_assert_ne", ], body, ); + panics.retain(|span| is_expn_of(*span, "debug_assert").is_none()); if !panics.is_empty() { span_lint_and_then( cx, diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs index 1e94685894723..1a680e7607e0b 100644 --- a/clippy_lints/src/panic_unimplemented.rs +++ b/clippy_lints/src/panic_unimplemented.rs @@ -74,7 +74,7 @@ declare_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANI impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if match_panic_call(cx, expr).is_some() { + if match_panic_call(cx, expr).is_some() && is_expn_of(expr.span, "debug_assert").is_none() { let span = get_outer_span(expr); if is_expn_of(expr.span, "unimplemented").is_some() { span_lint( diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 6f80e447c7ddd..6b64846c24d10 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -42,6 +42,14 @@ declare_clippy_lint! { /// false positives in cases involving multiple lifetimes that are bounded by /// each other. /// + /// Also, it does not take account of other similar cases where getting memory addresses + /// matters; namely, returning the pointer to the argument in question, + /// and passing the argument, as both references and pointers, + /// to a function that needs the memory address. For further details, refer to + /// [this issue](https://github.com/rust-lang/rust-clippy/issues/5953) + /// that explains a real case in which this false positive + /// led to an **undefined behaviour** introduced with unsafe code. + /// /// **Example:** /// /// ```rust diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 09fcdb5faf842..b0674f9067836 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_the use clippy_utils::ptr::get_spans; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{is_type_diagnostic_item, match_type, walk_ptrs_hir_ty}; -use clippy_utils::{is_allowed, match_qpath, paths}; +use clippy_utils::{expr_path_res, is_allowed, match_any_def_paths, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{ @@ -15,6 +15,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; +use rustc_span::symbol::Symbol; use rustc_span::{sym, MultiSpan}; use std::borrow::Cow; @@ -94,7 +95,7 @@ declare_clippy_lint! { /// ``` pub CMP_NULL, style, - "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead." + "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead" } declare_clippy_lint! { @@ -119,7 +120,28 @@ declare_clippy_lint! { "fns that create mutable refs from immutable ref args" } -declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF]); +declare_clippy_lint! { + /// **What it does:** This lint checks for invalid usages of `ptr::null`. + /// + /// **Why is this bad?** This causes undefined behavior. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```ignore + /// // Bad. Undefined behavior + /// unsafe { std::slice::from_raw_parts(ptr::null(), 0); } + /// ``` + /// + /// // Good + /// unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); } + /// ``` + pub INVALID_NULL_PTR_USAGE, + correctness, + "invalid usage of a null pointer, suggesting `NonNull::dangling()` instead" +} + +declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, INVALID_NULL_PTR_USAGE]); impl<'tcx> LateLintPass<'tcx> for Ptr { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { @@ -153,7 +175,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { 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(l) || is_null_path(r)) { + if (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) && (is_null_path(cx, l) || is_null_path(cx, r)) { span_lint( cx, CMP_NULL, @@ -161,6 +183,55 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { "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(ref fun, ref 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::>(); + 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, + ); + } + } } } } @@ -345,13 +416,12 @@ fn get_rptr_lm<'tcx>(ty: &'tcx Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, } } -fn is_null_path(expr: &Expr<'_>) -> bool { - if let ExprKind::Call(pathexp, args) = expr.kind { - if args.is_empty() { - if let ExprKind::Path(ref path) = pathexp.kind { - return match_qpath(path, &paths::PTR_NULL) || match_qpath(path, &paths::PTR_NULL_MUT); - } - } +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_def_paths(cx, id, &[&paths::PTR_NULL, &paths::PTR_NULL_MUT]).is_some() + }) + } else { + false } - false } diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 6d720f43851a0..30bee21390068 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -1,12 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +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, match_def_path, match_qpath, paths}; +use clippy_utils::{eq_expr_value, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{def, BindingAnnotation, Block, Expr, ExprKind, MatchSource, PatKind, StmtKind}; +use rustc_hir::LangItem::{OptionNone, OptionSome}; +use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, MatchSource, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -100,15 +101,14 @@ impl QuestionMark { if Self::is_option(cx, subject); if let PatKind::TupleStruct(path1, fields, None) = &arms[0].pat.kind; - if match_qpath(path1, &["Some"]); - if let PatKind::Binding(annot, _, bind, _) = &fields[0].kind; + if is_lang_ctor(cx, path1, OptionSome); + 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) = &arms[0].body.kind; if block.stmts.is_empty(); if let Some(trailing_expr) = &block.expr; - if let ExprKind::Path(path) = &trailing_expr.kind; - if match_qpath(path, &[&bind.as_str()]); + if path_to_local_id(trailing_expr, bind_id); if let PatKind::Wild = arms[1].pat.kind; if Self::expression_returns_none(cx, arms[1].body); @@ -156,15 +156,7 @@ impl QuestionMark { false }, ExprKind::Ret(Some(expr)) => Self::expression_returns_none(cx, expr), - ExprKind::Path(ref qp) => { - if let Res::Def(DefKind::Ctor(def::CtorOf::Variant, def::CtorKind::Const), def_id) = - cx.qpath_res(qp, expression.hir_id) - { - return match_def_path(cx, def_id, &paths::OPTION_NONE); - } - - false - }, + ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), _ => false, } } diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index af772cf4a145d..b565c77aaecff 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_opt; -use clippy_utils::{fn_def_id, in_macro, match_qpath}; +use clippy_utils::{fn_def_id, in_macro, path_to_local_id}; use if_chain::if_chain; use rustc_ast::ast::Attribute; use rustc_errors::Applicability; @@ -84,9 +84,8 @@ impl<'tcx> LateLintPass<'tcx> for Return { if local.ty.is_none(); if cx.tcx.hir().attrs(local.hir_id).is_empty(); if let Some(initexpr) = &local.init; - if let PatKind::Binding(.., ident, _) = local.pat.kind; - if let ExprKind::Path(qpath) = &retexpr.kind; - if match_qpath(qpath, &[&*ident.name.as_str()]); + if let PatKind::Binding(_, local_id, _, _) = local.pat.kind; + if path_to_local_id(retexpr, local_id); if !last_statement_borrows(cx, initexpr); if !in_external_macro(cx.sess(), initexpr.span); if !in_external_macro(cx.sess(), retexpr.span); @@ -223,6 +222,7 @@ fn check_final_expr<'tcx>( }, _ => (), }, + ExprKind::DropTemps(expr) => check_final_expr(cx, expr, None, RetReplacement::Empty), _ => (), } } diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index c9d72aabb6a3c..6104103580e98 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -1,11 +1,10 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::in_macro; -use if_chain::if_chain; -use rustc_ast::{Item, ItemKind, UseTreeKind}; +use rustc_ast::{ptr::P, Crate, Item, ItemKind, ModKind, UseTreeKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::edition::Edition; +use rustc_span::{edition::Edition, symbol::kw, Span, Symbol}; declare_clippy_lint! { /// **What it does:** Checking for imports with single component use path. @@ -38,26 +37,120 @@ declare_clippy_lint! { declare_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]); impl EarlyLintPass for SingleComponentPathImports { - fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if_chain! { - if !in_macro(item.span); - if cx.sess.opts.edition >= Edition::Edition2018; - if !item.vis.kind.is_pub(); - if let ItemKind::Use(use_tree) = &item.kind; - if let segments = &use_tree.prefix.segments; - if segments.len() == 1; - if let UseTreeKind::Simple(None, _, _) = use_tree.kind; - then { + fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) { + if cx.sess.opts.edition < Edition::Edition2018 { + return; + } + check_mod(cx, &krate.items); + } +} + +fn check_mod(cx: &EarlyContext<'_>, items: &[P]) { + // keep track of imports reused with `self` keyword, + // such as `self::crypto_hash` in the example below + // ```rust,ignore + // use self::crypto_hash::{Algorithm, Hasher}; + // ``` + let mut imports_reused_with_self = Vec::new(); + + // keep track of single use statements + // such as `crypto_hash` in the example below + // ```rust,ignore + // use crypto_hash; + // ``` + let mut single_use_usages = Vec::new(); + + for item in items { + track_uses(cx, &item, &mut imports_reused_with_self, &mut single_use_usages); + } + + for single_use in &single_use_usages { + if !imports_reused_with_self.contains(&single_use.0) { + let can_suggest = single_use.2; + if can_suggest { span_lint_and_sugg( cx, SINGLE_COMPONENT_PATH_IMPORTS, - item.span, + single_use.1, "this import is redundant", "remove it entirely", String::new(), - Applicability::MachineApplicable + Applicability::MachineApplicable, + ); + } else { + span_lint_and_help( + cx, + SINGLE_COMPONENT_PATH_IMPORTS, + single_use.1, + "this import is redundant", + None, + "remove this import", ); } } } } + +fn track_uses( + cx: &EarlyContext<'_>, + item: &Item, + imports_reused_with_self: &mut Vec, + single_use_usages: &mut Vec<(Symbol, Span, bool)>, +) { + if in_macro(item.span) || item.vis.kind.is_pub() { + return; + } + + match &item.kind { + ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => { + check_mod(cx, &items); + }, + ItemKind::Use(use_tree) => { + let segments = &use_tree.prefix.segments; + + // keep track of `use some_module;` usages + if segments.len() == 1 { + if let UseTreeKind::Simple(None, _, _) = use_tree.kind { + let ident = &segments[0].ident; + single_use_usages.push((ident.name, item.span, true)); + } + return; + } + + if segments.is_empty() { + // keep track of `use {some_module, some_other_module};` usages + if let UseTreeKind::Nested(trees) = &use_tree.kind { + for tree in trees { + let segments = &tree.0.prefix.segments; + if segments.len() == 1 { + if let UseTreeKind::Simple(None, _, _) = tree.0.kind { + let ident = &segments[0].ident; + single_use_usages.push((ident.name, tree.0.span, false)); + } + } + } + } + } else { + // keep track of `use self::some_module` usages + if segments[0].ident.name == kw::SelfLower { + // simple case such as `use self::module::SomeStruct` + if segments.len() > 1 { + imports_reused_with_self.push(segments[1].ident.name); + return; + } + + // nested case such as `use self::{module1::Struct1, module2::Struct2}` + if let UseTreeKind::Nested(trees) = &use_tree.kind { + for tree in trees { + let segments = &tree.0.prefix.segments; + if !segments.is_empty() { + imports_reused_with_self.push(segments[0].ident.name); + } + } + } + } + } + }, + _ => {}, + } +} diff --git a/clippy_lints/src/size_of_in_element_count.rs b/clippy_lints/src/size_of_in_element_count.rs index 09e0086681556..cd2bdec1707b3 100644 --- a/clippy_lints/src/size_of_in_element_count.rs +++ b/clippy_lints/src/size_of_in_element_count.rs @@ -65,8 +65,8 @@ fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { const FUNCTIONS: [&[&str]; 8] = [ - &paths::COPY_NONOVERLAPPING, - &paths::COPY, + &paths::PTR_COPY_NONOVERLAPPING, + &paths::PTR_COPY, &paths::WRITE_BYTES, &paths::PTR_SWAP_NONOVERLAPPING, &paths::PTR_SLICE_FROM_RAW_PARTS, diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index 8cf89ae456ee8..191781be000cf 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sugg::Sugg; -use clippy_utils::{get_enclosing_block, match_qpath, SpanlessEq}; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{get_enclosing_block, is_expr_path_def_path, path_to_local, path_to_local_id, paths, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -9,7 +10,7 @@ use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, PatKind, QPath, use rustc_lint::{LateContext, LateLintPass, Lint}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::symbol::Symbol; +use rustc_span::symbol::sym; declare_clippy_lint! { /// **What it does:** Checks slow zero-filled vector initialization @@ -46,8 +47,8 @@ declare_lint_pass!(SlowVectorInit => [SLOW_VECTOR_INITIALIZATION]); /// assigned to a variable. For example, `let mut vec = Vec::with_capacity(0)` or /// `vec = Vec::with_capacity(0)` struct VecAllocation<'tcx> { - /// Symbol of the local variable name - variable_name: Symbol, + /// HirId of the variable + local_id: HirId, /// Reference to the expression which allocates the vector allocation_expr: &'tcx Expr<'tcx>, @@ -72,16 +73,15 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit { if_chain! { if let ExprKind::Assign(left, right, _) = expr.kind; - // Extract variable name - if let ExprKind::Path(QPath::Resolved(_, path)) = left.kind; - if let Some(variable_name) = path.segments.get(0); + // Extract variable + if let Some(local_id) = path_to_local(left); // Extract len argument - if let Some(len_arg) = Self::is_vec_with_capacity(right); + if let Some(len_arg) = Self::is_vec_with_capacity(cx, right); then { let vi = VecAllocation { - variable_name: variable_name.ident.name, + local_id, allocation_expr: right, len_expr: len_arg, }; @@ -95,13 +95,13 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit { // Matches statements which initializes vectors. For example: `let mut vec = Vec::with_capacity(10)` if_chain! { if let StmtKind::Local(local) = stmt.kind; - if let PatKind::Binding(BindingAnnotation::Mutable, .., variable_name, None) = local.pat.kind; + if let PatKind::Binding(BindingAnnotation::Mutable, local_id, _, None) = local.pat.kind; if let Some(init) = local.init; - if let Some(len_arg) = Self::is_vec_with_capacity(init); + if let Some(len_arg) = Self::is_vec_with_capacity(cx, init); then { let vi = VecAllocation { - variable_name: variable_name.name, + local_id, allocation_expr: init, len_expr: len_arg, }; @@ -115,19 +115,18 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit { impl SlowVectorInit { /// Checks if the given expression is `Vec::with_capacity(..)`. It will return the expression /// of the first argument of `with_capacity` call if it matches or `None` if it does not. - fn is_vec_with_capacity<'tcx>(expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { + fn is_vec_with_capacity<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { if_chain! { - if let ExprKind::Call(func, args) = expr.kind; - if let ExprKind::Path(ref path) = func.kind; - if match_qpath(path, &["Vec", "with_capacity"]); - if args.len() == 1; - + if let ExprKind::Call(func, [arg]) = expr.kind; + if let ExprKind::Path(QPath::TypeRelative(ty, name)) = func.kind; + if name.ident.as_str() == "with_capacity"; + if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::vec_type); then { - return Some(&args[0]); + Some(arg) + } else { + None } } - - None } /// Search initialization for the given vector @@ -208,11 +207,9 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { fn search_slow_extend_filling(&mut self, expr: &'tcx Expr<'_>) { if_chain! { if self.initialization_found; - if let ExprKind::MethodCall(path, _, args, _) = expr.kind; - if let ExprKind::Path(ref qpath_subj) = args[0].kind; - if match_qpath(qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]); + if let ExprKind::MethodCall(path, _, [self_arg, extend_arg], _) = expr.kind; + if path_to_local_id(self_arg, self.vec_alloc.local_id); if path.ident.name == sym!(extend); - if let Some(extend_arg) = args.get(1); if self.is_repeat_take(extend_arg); then { @@ -225,11 +222,9 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'_>) { if_chain! { if self.initialization_found; - if let ExprKind::MethodCall(path, _, args, _) = expr.kind; - if let ExprKind::Path(ref qpath_subj) = args[0].kind; - if match_qpath(qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]); + if let ExprKind::MethodCall(path, _, [self_arg, len_arg, fill_arg], _) = expr.kind; + if path_to_local_id(self_arg, self.vec_alloc.local_id); if path.ident.name == sym!(resize); - if let (Some(len_arg), Some(fill_arg)) = (args.get(1), args.get(2)); // Check that is filled with 0 if let ExprKind::Lit(ref lit) = fill_arg.kind; @@ -252,7 +247,7 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { // Check that take is applied to `repeat(0)` if let Some(repeat_expr) = take_args.get(0); - if Self::is_repeat_zero(repeat_expr); + if self.is_repeat_zero(repeat_expr); // Check that len expression is equals to `with_capacity` expression if let Some(len_arg) = take_args.get(1); @@ -267,21 +262,19 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { } /// Returns `true` if given expression is `repeat(0)` - fn is_repeat_zero(expr: &Expr<'_>) -> bool { + fn is_repeat_zero(&self, expr: &Expr<'_>) -> bool { if_chain! { - if let ExprKind::Call(fn_expr, repeat_args) = expr.kind; - if let ExprKind::Path(ref qpath_repeat) = fn_expr.kind; - if match_qpath(qpath_repeat, &["repeat"]); - if let Some(repeat_arg) = repeat_args.get(0); + if let ExprKind::Call(fn_expr, [repeat_arg]) = expr.kind; + if is_expr_path_def_path(self.cx, fn_expr, &paths::ITER_REPEAT); if let ExprKind::Lit(ref lit) = repeat_arg.kind; if let LitKind::Int(0, _) = lit.node; then { - return true + true + } else { + false } } - - false } } diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index cb2237e531262..4272935bc310e 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -195,7 +195,7 @@ fn attempt_to_emit_no_difference_lint( i: usize, expected_loc: IdentLocation, ) { - if let Some(binop) = binops.get(i).cloned() { + if let Some(binop) = binops.get(i).copied() { // We need to try and figure out which identifier we should // suggest using instead. Since there could be multiple // replacement candidates in a given expression, and we're diff --git a/clippy_lints/src/tabs_in_doc_comments.rs b/clippy_lints/src/tabs_in_doc_comments.rs index a0492a88f912a..e2c144709f5b7 100644 --- a/clippy_lints/src/tabs_in_doc_comments.rs +++ b/clippy_lints/src/tabs_in_doc_comments.rs @@ -104,30 +104,32 @@ fn get_chunks_of_tabs(the_str: &str) -> Vec<(u32, u32)> { // tracker to decide if the last group of tabs is not closed by a non-tab character let mut is_active = false; - let chars_array: Vec<_> = the_str.chars().collect(); + // Note that we specifically need the char _byte_ indices here, not the positional indexes + // within the char array to deal with multi-byte characters properly. `char_indices` does + // exactly that. It provides an iterator over tuples of the form `(byte position, char)`. + let char_indices: Vec<_> = the_str.char_indices().collect(); - if chars_array == vec!['\t'] { + if let [(_, '\t')] = char_indices.as_slice() { return vec![(0, 1)]; } - for (index, arr) in chars_array.windows(2).enumerate() { - let index = u32::try_from(index).expect(line_length_way_to_long); - match arr { - ['\t', '\t'] => { + for entry in char_indices.windows(2) { + match entry { + [(_, '\t'), (_, '\t')] => { // either string starts with double tab, then we have to set it active, // otherwise is_active is true anyway is_active = true; }, - [_, '\t'] => { + [(_, _), (index_b, '\t')] => { // as ['\t', '\t'] is excluded, this has to be a start of a tab group, // set indices accordingly is_active = true; - current_start = index + 1; + current_start = u32::try_from(*index_b).unwrap(); }, - ['\t', _] => { + [(_, '\t'), (index_b, _)] => { // this now has to be an end of the group, hence we have to push a new tuple is_active = false; - spans.push((current_start, index + 1)); + spans.push((current_start, u32::try_from(*index_b).unwrap())); }, _ => {}, } @@ -137,7 +139,7 @@ fn get_chunks_of_tabs(the_str: &str) -> Vec<(u32, u32)> { if is_active { spans.push(( current_start, - u32::try_from(the_str.chars().count()).expect(line_length_way_to_long), + u32::try_from(char_indices.last().unwrap().0 + 1).expect(line_length_way_to_long), )); } @@ -148,6 +150,13 @@ fn get_chunks_of_tabs(the_str: &str) -> Vec<(u32, u32)> { mod tests_for_get_chunks_of_tabs { use super::get_chunks_of_tabs; + #[test] + fn test_unicode_han_string() { + let res = get_chunks_of_tabs(" \u{4f4d}\t"); + + assert_eq!(res, vec![(4, 5)]); + } + #[test] fn test_empty_string() { let res = get_chunks_of_tabs(""); diff --git a/clippy_lints/src/to_string_in_display.rs b/clippy_lints/src/to_string_in_display.rs index ae05a8da37bc9..4fb297ac6c699 100644 --- a/clippy_lints/src/to_string_in_display.rs +++ b/clippy_lints/src/to_string_in_display.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::{is_diagnostic_assoc_item, match_def_path, path_to_local_id, paths}; +use clippy_utils::{is_diag_trait_item, match_def_path, path_to_local_id, paths}; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -95,7 +95,7 @@ impl LateLintPass<'_> for ToStringInDisplay { if let ExprKind::MethodCall(path, _, args, _) = expr.kind; if path.ident.name == sym!(to_string); if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if is_diagnostic_assoc_item(cx, expr_def_id, sym::ToString); + if is_diag_trait_item(cx, expr_def_id, sym::ToString); if path_to_local_id(&args[0], self_hir_id); then { span_lint( diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 86ac916df6cba..569113910c982 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -274,7 +274,7 @@ declare_clippy_lint! { /// let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) }; /// ``` pub TRANSMUTE_PTR_TO_PTR, - complexity, + pedantic, "transmutes from a pointer to a pointer / a reference to a reference" } diff --git a/clippy_lints/src/transmuting_null.rs b/clippy_lints/src/transmuting_null.rs index 0be05d3e0cf3f..888ecab10461a 100644 --- a/clippy_lints/src/transmuting_null.rs +++ b/clippy_lints/src/transmuting_null.rs @@ -1,6 +1,6 @@ use crate::consts::{constant_context, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::{match_qpath, paths}; +use clippy_utils::{is_expr_path_def_path, paths}; use if_chain::if_chain; use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind}; @@ -37,18 +37,15 @@ impl<'tcx> LateLintPass<'tcx> for TransmutingNull { } if_chain! { - if let ExprKind::Call(func, args) = expr.kind; - if let ExprKind::Path(ref path) = func.kind; - if match_qpath(path, &paths::STD_MEM_TRANSMUTE); - if args.len() == 1; + if let ExprKind::Call(func, [arg]) = expr.kind; + if is_expr_path_def_path(cx, func, &paths::TRANSMUTE); then { - // Catching transmute over constants that resolve to `null`. let mut const_eval_context = constant_context(cx, cx.typeck_results()); if_chain! { - if let ExprKind::Path(ref _qpath) = args[0].kind; - let x = const_eval_context.expr(&args[0]); + if let ExprKind::Path(ref _qpath) = arg.kind; + let x = const_eval_context.expr(arg); if let Some(Constant::RawPtr(0)) = x; then { span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG) @@ -58,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for TransmutingNull { // Catching: // `std::mem::transmute(0 as *const i32)` if_chain! { - if let ExprKind::Cast(inner_expr, _cast_ty) = args[0].kind; + if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind; if let ExprKind::Lit(ref lit) = inner_expr.kind; if let LitKind::Int(0, _) = lit.node; then { @@ -69,10 +66,8 @@ impl<'tcx> LateLintPass<'tcx> for TransmutingNull { // Catching: // `std::mem::transmute(std::ptr::null::())` if_chain! { - if let ExprKind::Call(func1, args1) = args[0].kind; - if let ExprKind::Path(ref path1) = func1.kind; - if match_qpath(path1, &paths::STD_PTR_NULL); - if args1.is_empty(); + if let ExprKind::Call(func1, []) = arg.kind; + if is_expr_path_def_path(cx, func1, &paths::PTR_NULL); then { span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG) } diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs index 23a1953ffaceb..ebb39ea4877de 100644 --- a/clippy_lints/src/try_err.rs +++ b/clippy_lints/src/try_err.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet, snippet_with_macro_callsite}; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{differing_macro_contexts, in_macro, match_def_path, match_qpath, paths}; +use clippy_utils::{differing_macro_contexts, get_parent_expr, in_macro, is_lang_ctor, match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; +use rustc_hir::LangItem::ResultErr; use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; @@ -68,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { if let ExprKind::Call(err_fun, err_args) = try_arg.kind; if let Some(err_arg) = err_args.get(0); if let ExprKind::Path(ref err_fun_path) = err_fun.kind; - if match_qpath(err_fun_path, &paths::RESULT_ERR); + if is_lang_ctor(cx, err_fun_path, ResultErr); if let Some(return_ty) = find_return_type(cx, &expr.kind); then { let prefix; @@ -101,10 +102,15 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { } else { snippet(cx, err_arg.span, "_") }; + let ret_prefix = if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::Ret(_))) { + "" // already returns + } else { + "return " + }; let suggestion = if err_ty == expr_err_ty { - format!("return {}{}{}", prefix, origin_snippet, suffix) + format!("{}{}{}{}", ret_prefix, prefix, origin_snippet, suffix) } else { - format!("return {}{}.into(){}", prefix, origin_snippet, suffix) + format!("{}{}{}.into(){}", ret_prefix, prefix, origin_snippet, suffix) }; span_lint_and_sugg( diff --git a/clippy_lints/src/types/borrowed_box.rs b/clippy_lints/src/types/borrowed_box.rs index 1425d8f3f37ed..bdeff035e5ec9 100644 --- a/clippy_lints/src/types/borrowed_box.rs +++ b/clippy_lints/src/types/borrowed_box.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::{match_path, paths}; +use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{ @@ -28,7 +28,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m _ => None, }); then { - if is_any_trait(inner) { + if is_any_trait(cx, inner) { // Ignore `Box` types; see issue #1884 for details. return false; } @@ -84,13 +84,14 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m } // Returns true if given type is `Any` trait. -fn is_any_trait(t: &hir::Ty<'_>) -> bool { +fn is_any_trait(cx: &LateContext<'_>, t: &hir::Ty<'_>) -> bool { if_chain! { if let TyKind::TraitObject(traits, ..) = t.kind; if !traits.is_empty(); + if let Some(trait_did) = traits[0].trait_ref.trait_def_id(); // Only Send/Sync can be used as additional traits, so it is enough to // check only the first trait. - if match_path(traits[0].trait_ref.path, &paths::ANY_TRAIT); + if match_def_path(cx, trait_did, &paths::ANY_TRAIT); then { return true; } diff --git a/clippy_lints/src/unnecessary_self_imports.rs b/clippy_lints/src/unnecessary_self_imports.rs new file mode 100644 index 0000000000000..48c54d79cf113 --- /dev/null +++ b/clippy_lints/src/unnecessary_self_imports.rs @@ -0,0 +1,67 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use if_chain::if_chain; +use rustc_ast::{Item, ItemKind, UseTreeKind}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::kw; + +declare_clippy_lint! { + /// **What it does:** Checks for imports ending in `::{self}`. + /// + /// **Why is this bad?** In most cases, this can be written much more cleanly by omitting `::{self}`. + /// + /// **Known problems:** Removing `::{self}` will cause any non-module items at the same path to also be imported. + /// This might cause a naming conflict (https://github.com/rust-lang/rustfmt/issues/3568). This lint makes no attempt + /// to detect this scenario and that is why it is a restriction lint. + /// + /// **Example:** + /// + /// ```rust + /// use std::io::{self}; + /// ``` + /// Use instead: + /// ```rust + /// use std::io; + /// ``` + pub UNNECESSARY_SELF_IMPORTS, + restriction, + "imports ending in `::{self}`, which can be omitted" +} + +declare_lint_pass!(UnnecessarySelfImports => [UNNECESSARY_SELF_IMPORTS]); + +impl EarlyLintPass for UnnecessarySelfImports { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if_chain! { + if let ItemKind::Use(use_tree) = &item.kind; + if let UseTreeKind::Nested(nodes) = &use_tree.kind; + if let [(self_tree, _)] = &**nodes; + if let [self_seg] = &*self_tree.prefix.segments; + if self_seg.ident.name == kw::SelfLower; + if let Some(last_segment) = use_tree.prefix.segments.last(); + + then { + span_lint_and_then( + cx, + UNNECESSARY_SELF_IMPORTS, + item.span, + "import ending with `::{self}`", + |diag| { + diag.span_suggestion( + last_segment.span().with_hi(item.span.hi()), + "consider omitting `::{self}`", + format!( + "{}{};", + last_segment.ident, + if let UseTreeKind::Simple(Some(alias), ..) = self_tree.kind { format!(" as {}", alias) } else { String::new() }, + ), + Applicability::MaybeIncorrect, + ); + diag.note("this will slightly change semantics; any non-module items at the same path will also be imported"); + }, + ); + } + } + } +} diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 5bb417cb1be4b..f2f1410aed742 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; -use clippy_utils::{contains_return, in_macro, match_qpath, paths, return_ty, visitors::find_all_ret_expressions}; +use clippy_utils::{contains_return, in_macro, is_lang_ctor, return_ty, visitors::find_all_ret_expressions}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; +use rustc_hir::LangItem::{OptionSome, ResultOk}; use rustc_hir::{Body, ExprKind, FnDecl, HirId, Impl, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -85,11 +86,11 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { } // Get the wrapper and inner types, if can't, abort. - let (return_type_label, path, inner_type) = if let ty::Adt(adt_def, subst) = return_ty(cx, hir_id).kind() { + let (return_type_label, lang_item, inner_type) = if let ty::Adt(adt_def, subst) = return_ty(cx, hir_id).kind() { if cx.tcx.is_diagnostic_item(sym::option_type, adt_def.did) { - ("Option", &paths::OPTION_SOME, subst.type_at(0)) + ("Option", OptionSome, subst.type_at(0)) } else if cx.tcx.is_diagnostic_item(sym::result_type, adt_def.did) { - ("Result", &paths::RESULT_OK, subst.type_at(0)) + ("Result", ResultOk, subst.type_at(0)) } else { return; } @@ -103,14 +104,12 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { if_chain! { if !in_macro(ret_expr.span); // Check if a function call. - if let ExprKind::Call(func, args) = ret_expr.kind; - // Get the Path of the function call. - if let ExprKind::Path(ref qpath) = func.kind; + if let ExprKind::Call(func, [arg]) = ret_expr.kind; // Check if OPTION_SOME or RESULT_OK, depending on return type. - if match_qpath(qpath, path); - if args.len() == 1; + if let ExprKind::Path(qpath) = &func.kind; + if is_lang_ctor(cx, qpath, lang_item); // Make sure the function argument does not contain a return expression. - if !contains_return(&args[0]); + if !contains_return(arg); then { suggs.push( ( @@ -118,7 +117,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { if inner_type.is_unit() { "".to_string() } else { - snippet(cx, args[0].span.source_callsite(), "..").to_string() + snippet(cx, arg.span.source_callsite(), "..").to_string() } ) ); diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 024ab03fd418e..5e8e530f480fb 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -41,7 +41,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { }; match expr.kind { - hir::ExprKind::Match(res, _, _) if is_try(expr).is_some() => { + hir::ExprKind::Match(res, _, _) if is_try(cx, expr).is_some() => { if let hir::ExprKind::Call(func, args) = res.kind { if matches!( func.kind, diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index cf8039d6059b6..3d3d0e19d2622 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -3,7 +3,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sug use clippy_utils::source::snippet; use clippy_utils::ty::match_type; use clippy_utils::{ - is_else_clause, is_expn_of, match_def_path, match_qpath, method_calls, path_to_res, paths, run_lints, SpanlessEq, + is_else_clause, is_expn_of, is_expr_path_def_path, match_def_path, method_calls, path_to_res, paths, run_lints, + SpanlessEq, }; use if_chain::if_chain; use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, ModKind, NodeId}; @@ -578,8 +579,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { if_chain! { if let ExprKind::Call(func, and_then_args) = expr.kind; - if let ExprKind::Path(ref path) = func.kind; - if match_qpath(path, &["span_lint_and_then"]); + if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]); if and_then_args.len() == 5; if let ExprKind::Closure(_, _, body_id, _, _) = &and_then_args[4].kind; let body = cx.tcx.hir().body(*body_id); @@ -761,8 +761,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem { if_chain! { // Check if this is a call to utils::match_type() if let ExprKind::Call(fn_path, [context, ty, ty_path]) = expr.kind; - if let ExprKind::Path(fn_qpath) = &fn_path.kind; - if match_qpath(fn_qpath, &["utils", "match_type"]); + if is_expr_path_def_path(cx, fn_path, &["clippy_utils", "ty", "match_type"]); // Extract the path to the matched type if let Some(segments) = path_to_matched_type(cx, ty_path); let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect(); @@ -771,6 +770,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem { let diag_items = cx.tcx.diagnostic_items(ty_did.krate); if let Some(item_name) = diag_items.iter().find_map(|(k, v)| if *v == ty_did { Some(k) } else { None }); then { + // TODO: check paths constants from external crates. let cx_snippet = snippet(cx, context.span, "_"); let ty_snippet = snippet(cx, ty.span, "_"); @@ -778,9 +778,9 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem { cx, MATCH_TYPE_ON_DIAGNOSTIC_ITEM, expr.span, - "usage of `utils::match_type()` on a type diagnostic item", + "usage of `clippy_utils::ty::match_type()` on a type diagnostic item", "try", - format!("utils::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name), + format!("clippy_utils::ty::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name), Applicability::MaybeIncorrect, ); } diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 12a47a6b7036d..7e962472c07f5 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -573,7 +573,7 @@ impl Write { diag.multipart_suggestion( "try this", iter::once((comma_span.to(token_expr.span), String::new())) - .chain(fmt_spans.iter().cloned().zip(iter::repeat(replacement))) + .chain(fmt_spans.iter().copied().zip(iter::repeat(replacement))) .collect(), Applicability::MachineApplicable, ); diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index eaea3e636f9c3..93e10c836cc7f 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -5,6 +5,7 @@ #![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)] use crate::{both, over}; +use if_chain::if_chain; use rustc_ast::ptr::P; use rustc_ast::{self as ast, *}; use rustc_span::symbol::Ident; @@ -571,3 +572,34 @@ pub fn eq_mac_args(l: &MacArgs, r: &MacArgs) -> bool { _ => false, } } + +/// Extract args from an assert-like macro. +/// +/// Currently working with: +/// - `assert_eq!` and `assert_ne!` +/// - `debug_assert_eq!` and `debug_assert_ne!` +/// +/// For example: +/// +/// `debug_assert_eq!(a, b)` will return Some([a, b]) +pub fn extract_assert_macro_args(mut expr: &Expr) -> Option<[&Expr; 2]> { + if_chain! { + if let ExprKind::If(_, ref block, _) = expr.kind; + if let StmtKind::Semi(ref e) = block.stmts.get(0)?.kind; + then { + expr = e; + } + } + if_chain! { + if let ExprKind::Block(ref block, _) = expr.kind; + if let StmtKind::Expr(ref expr) = block.stmts.get(0)?.kind; + if let ExprKind::Match(ref match_expr, _) = expr.kind; + if let ExprKind::Tup(ref tup) = match_expr.kind; + if let [a, b, ..] = tup.as_slice(); + if let (&ExprKind::AddrOf(_, _, ref a), &ExprKind::AddrOf(_, _, ref b)) = (&a.kind, &b.kind); + then { + return Some([&*a, &*b]); + } + } + None +} diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index 7ec8452bf4c67..c0584e1e22694 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -151,10 +151,9 @@ pub fn is_proc_macro(sess: &Session, attrs: &[ast::Attribute]) -> bool { /// Return true if the attributes contain `#[doc(hidden)]` pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool { - #[allow(clippy::filter_map)] attrs .iter() .filter(|attr| attr.has_name(sym::doc)) - .flat_map(ast::Attribute::meta_item_list) + .filter_map(ast::Attribute::meta_item_list) .any(|l| attr::list_contains_name(&l, sym::hidden)) } diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index f695f1a61e716..07ae6e924e28b 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -96,6 +96,16 @@ impl HirEqInterExpr<'_, '_, '_> { pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool { match (&left.kind, &right.kind) { (&StmtKind::Local(ref l), &StmtKind::Local(ref r)) => { + // This additional check ensures that the type of the locals are equivalent even if the init + // expression or type have some inferred parts. + if let Some(typeck) = self.inner.maybe_typeck_results { + let l_ty = typeck.pat_ty(&l.pat); + let r_ty = typeck.pat_ty(&r.pat); + if !rustc_middle::ty::TyS::same_type(l_ty, r_ty) { + return false; + } + } + // eq_pat adds the HirIds to the locals map. We therefor call it last to make sure that // these only get added if the init and type is equal. both(&l.init, &r.init, |l, r| self.eq_expr(l, r)) @@ -424,7 +434,7 @@ fn reduce_exprkind<'hir>(cx: &LateContext<'_>, kind: &'hir ExprKind<'hir>) -> &' TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace ) }) - .ne([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().cloned()) => + .ne([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().copied()) => { kind }, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index c847712ec2e9e..9564432ee48e1 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -10,6 +10,7 @@ // (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; @@ -57,13 +58,14 @@ use if_chain::if_chain; use rustc_ast::ast::{self, Attribute, BorrowKind, LitKind}; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; -use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visitor}; +use rustc_hir::LangItem::{ResultErr, ResultOk}; use rustc_hir::{ - def, Arm, BindingAnnotation, Block, Body, Constness, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, ImplItem, - ImplItemKind, Item, ItemKind, LangItem, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment, QPath, - TraitItem, TraitItemKind, TraitRef, TyKind, + def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, + ImplItem, ImplItemKind, Item, ItemKind, LangItem, Local, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment, + QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, }; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::exports::Export; @@ -80,7 +82,7 @@ use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::Integer; use crate::consts::{constant, Constant}; -use crate::ty::is_recursively_primitive_type; +use crate::ty::{can_partially_move_ty, is_recursively_primitive_type}; pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Option { if let Ok(version) = RustcVersion::parse(msrv) { @@ -222,6 +224,19 @@ pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool { } } +/// 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 { @@ -279,27 +294,29 @@ pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path)) } -/// Checks if the method call given in `def_id` belongs to a trait or other container with a given -/// diagnostic item -pub fn is_diagnostic_assoc_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool { - cx.tcx - .opt_associated_item(def_id) - .and_then(|associated_item| match associated_item.container { - rustc_ty::TraitContainer(assoc_def_id) => Some(assoc_def_id), - rustc_ty::ImplContainer(assoc_def_id) => match cx.tcx.type_of(assoc_def_id).kind() { - rustc_ty::Adt(adt, _) => Some(adt.did), - rustc_ty::Slice(_) => cx.tcx.get_diagnostic_item(sym::slice), // this isn't perfect but it works - _ => None, - }, - }) - .map_or(false, |assoc_def_id| cx.tcx.is_diagnostic_item(diag_item, assoc_def_id)) +/// 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_diagnostic_assoc_item(cx, did, diag_item)) + .map_or(false, |did| is_diag_trait_item(cx, did, diag_item)) } /// Checks if an expression references a variable of the given name. @@ -380,6 +397,29 @@ pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool { } } +/// 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. +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)) +} + /// 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()`. @@ -408,20 +448,6 @@ pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool { .all(|(a, b)| a.ident.name.as_str() == *b) } -/// Matches a `Path` against a slice of segment string literals, e.g. -/// -/// # Examples -/// ```rust,ignore -/// match_path_ast(path, &["std", "rt", "begin_unwind"]) -/// ``` -pub fn match_path_ast(path: &ast::Path, segments: &[&str]) -> bool { - path.segments - .iter() - .rev() - .zip(segments.iter().rev()) - .all(|(a, b)| a.ident.name.as_str() == *b) -} - /// If the expression is a path to a local, returns the canonical `HirId` of the local. pub fn path_to_local(expr: &Expr<'_>) -> Option { if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind { @@ -522,6 +548,73 @@ pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio None } +/// Checks if the top level expression can be moved into a closure as is. +pub fn can_move_expr_to_closure_no_visit(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, jump_targets: &[HirId]) -> bool { + match expr.kind { + ExprKind::Break(Destination { target_id: Ok(id), .. }, _) + | ExprKind::Continue(Destination { target_id: Ok(id), .. }) + if jump_targets.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(base_expr, _) + if matches!( + base_expr.kind, + ExprKind::Path(QPath::Resolved(_, Path { res: Res::Local(_), .. })) + ) && can_partially_move_ty(cx, cx.typeck_results().expr_ty(base_expr)) => + { + // TODO: check if the local has been partially moved. Assume it has for now. + false + } + _ => true, + } +} + +/// Checks if the expression can be moved into a closure as is. +pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { + struct V<'cx, 'tcx> { + cx: &'cx LateContext<'tcx>, + loops: Vec, + allow_closure: bool, + } + impl Visitor<'tcx> for V<'_, 'tcx> { + type Map = ErasedMap<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_expr(&mut self, e: &'tcx Expr<'_>) { + if !self.allow_closure { + return; + } + if let ExprKind::Loop(b, ..) = e.kind { + self.loops.push(e.hir_id); + self.visit_block(b); + self.loops.pop(); + } else { + self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops); + walk_expr(self, e); + } + } + } + + let mut v = V { + cx, + allow_closure: true, + loops: Vec::new(), + }; + v.visit_expr(expr); + v.allow_closure +} + /// 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>( @@ -960,7 +1053,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { /// 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().cloned().for_each(f) + pats.iter().copied().for_each(f) } else { f(pat) } @@ -1011,11 +1104,11 @@ pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl It /// Checks if a given expression is a match expression expanded from the `?` /// operator or the `try` macro. -pub fn is_try<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { - fn is_ok(arm: &Arm<'_>) -> bool { +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, ref pat, None) = arm.pat.kind; - if match_qpath(path, &paths::RESULT_OK[1..]); + 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 { @@ -1025,9 +1118,9 @@ pub fn is_try<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { false } - fn is_err(arm: &Arm<'_>) -> bool { + fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind { - match_qpath(path, &paths::RESULT_ERR[1..]) + is_lang_ctor(cx, path, ResultErr) } else { false } @@ -1043,8 +1136,8 @@ pub fn is_try<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { if arms.len() == 2; if arms[0].guard.is_none(); if arms[1].guard.is_none(); - if (is_ok(&arms[0]) && is_err(&arms[1])) || - (is_ok(&arms[1]) && is_err(&arms[0])); + 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); } @@ -1131,29 +1224,47 @@ pub fn match_function_call<'tcx>( None } +/// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if +/// any. +pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option { + 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 the path. pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool { - // We have to convert `syms` to `&[Symbol]` here because rustc's `match_def_path` - // accepts only that. We should probably move to Symbols in Clippy as well. - let syms = syms.iter().map(|p| Symbol::intern(p)).collect::>(); - cx.match_def_path(did, &syms) + // 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<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx [Expr<'tcx>]> { - match_function_call(cx, expr, &paths::BEGIN_PANIC) - .or_else(|| match_function_call(cx, expr, &paths::BEGIN_PANIC_FMT)) - .or_else(|| match_function_call(cx, expr, &paths::PANIC_ANY)) - .or_else(|| match_function_call(cx, expr, &paths::PANICKING_PANIC)) - .or_else(|| match_function_call(cx, expr, &paths::PANICKING_PANIC_FMT)) - .or_else(|| match_function_call(cx, expr, &paths::PANICKING_PANIC_STR)) +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_def_path(cx, did, &paths::BEGIN_PANIC) - || match_def_path(cx, did, &paths::BEGIN_PANIC_FMT) - || match_def_path(cx, did, &paths::PANIC_ANY) - || match_def_path(cx, did, &paths::PANICKING_PANIC) - || match_def_path(cx, did, &paths::PANICKING_PANIC_FMT) - || match_def_path(cx, did, &paths::PANICKING_PANIC_STR) + match_any_def_paths( + cx, + did, + &[ + &paths::BEGIN_PANIC, + &paths::BEGIN_PANIC_FMT, + &paths::PANIC_ANY, + &paths::PANICKING_PANIC, + &paths::PANICKING_PANIC_FMT, + &paths::PANICKING_PANIC_STR, + ], + ) + .is_some() } /// Returns the list of condition expressions and the list of blocks in a @@ -1189,21 +1300,6 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, (conds, blocks) } -/// This function returns true if the given expression is the `else` or `if else` part of an if -/// statement -pub fn parent_node_is_if_expr(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool { - let map = cx.tcx.hir(); - let parent_id = map.get_parent_node(expr.hir_id); - let parent_node = map.get(parent_id); - matches!( - parent_node, - Node::Expr(Expr { - kind: ExprKind::If(_, _, _), - .. - }) - ) -} - // 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)) @@ -1228,6 +1324,51 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { did.map_or(false, |did| must_use_attr(&cx.tcx.get_attrs(did)).is_some()) } +/// 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> { + let map = tcx.hir(); + let mut child_id = expr.hir_id; + let mut iter = map.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 { @@ -1397,28 +1538,43 @@ pub fn peel_hir_pat_refs(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) { 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) { - fn f(expr: &'a Expr<'a>, count: usize, target: usize) -> (&'a Expr<'a>, usize) { - match expr.kind { - ExprKind::AddrOf(_, _, expr) if count != target => f(expr, count + 1, target), - _ => (expr, count), - } - } - f(expr, 0, count) + let mut remaining = count; + let e = peel_hir_expr_while(expr, |e| match e.kind { + ExprKind::AddrOf(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) { - fn f(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) { - match expr.kind { - ExprKind::AddrOf(BorrowKind::Ref, _, expr) => f(expr, count + 1), - _ => (expr, count), - } - } - f(expr, 0) + let mut count = 0; + let e = peel_hir_expr_while(expr, |e| match e.kind { + ExprKind::AddrOf(BorrowKind::Ref, _, e) => { + count += 1; + Some(e) + }, + _ => None, + }); + (e, count) } #[macro_export] @@ -1450,27 +1606,3 @@ pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { } } } - -/// Check if the resolution of a given path is an `Ok` variant of `Result`. -pub fn is_ok_ctor(cx: &LateContext<'_>, res: Res) -> bool { - if let Some(ok_id) = cx.tcx.lang_items().result_ok_variant() { - if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { - if let Some(variant_id) = cx.tcx.parent(id) { - return variant_id == ok_id; - } - } - } - false -} - -/// Check if the resolution of a given path is a `Some` variant of `Option`. -pub fn is_some_ctor(cx: &LateContext<'_>, res: Res) -> bool { - if let Some(some_id) = cx.tcx.lang_items().option_some_variant() { - if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { - if let Some(variant_id) = cx.tcx.parent(id) { - return variant_id == some_id; - } - } - } - false -} diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 3b4c4070c0ed3..5e6733a300f2c 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -4,7 +4,7 @@ //! Whenever possible, please consider diagnostic items over hardcoded paths. //! See for more information. -pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"]; +pub const ANY_TRAIT: [&str; 3] = ["core", "any", "Any"]; pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"]; pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"]; pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"]; @@ -13,13 +13,13 @@ pub(super) const BEGIN_PANIC_FMT: [&str; 3] = ["std", "panicking", "begin_panic_ pub const BINARY_HEAP: [&str; 4] = ["alloc", "collections", "binary_heap", "BinaryHeap"]; pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"]; pub const BTREEMAP: [&str; 5] = ["alloc", "collections", "btree", "map", "BTreeMap"]; +pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"]; pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"]; +pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"]; pub const BTREESET: [&str; 5] = ["alloc", "collections", "btree", "set", "BTreeSet"]; pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"]; pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"]; pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"]; -pub const COPY: [&str; 4] = ["core", "intrinsics", "", "copy_nonoverlapping"]; -pub const COPY_NONOVERLAPPING: [&str; 4] = ["core", "intrinsics", "", "copy"]; pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"]; pub const CSTRING_AS_C_STR: [&str; 5] = ["std", "ffi", "c_str", "CString", "as_c_str"]; pub const DEFAULT_TRAIT: [&str; 3] = ["core", "default", "Default"]; @@ -44,10 +44,14 @@ pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"]; pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "FromIterator"]; +pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"]; +pub const FROM_STR_METHOD: [&str; 5] = ["core", "str", "traits", "FromStr", "from_str"]; pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; pub const HASH: [&str; 3] = ["core", "hash", "Hash"]; pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"]; +pub const HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"]; pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"]; +pub const HASHMAP_INSERT: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "insert"]; pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"]; #[cfg(feature = "internal-lints")] pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"]; @@ -60,8 +64,9 @@ pub const INTO: [&str; 3] = ["core", "convert", "Into"]; pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"]; pub const IO_READ: [&str; 3] = ["std", "io", "Read"]; pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"]; -pub const IPADDR_V4: [&str; 4] = ["std", "net", "IpAddr", "V4"]; -pub const IPADDR_V6: [&str; 4] = ["std", "net", "IpAddr", "V6"]; +pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"]; +pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"]; +pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"]; #[cfg(feature = "internal-lints")] pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; #[cfg(feature = "internal-lints")] @@ -100,12 +105,23 @@ pub const PERMISSIONS_FROM_MODE: [&str; 7] = ["std", "sys", "unix", "ext", "fs", pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"]; pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"]; pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"]; +pub const PTR_COPY: [&str; 4] = ["core", "intrinsics", "", "copy"]; +pub const PTR_COPY_NONOVERLAPPING: [&str; 4] = ["core", "intrinsics", "", "copy_nonoverlapping"]; pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"]; pub const PTR_NULL: [&str; 3] = ["core", "ptr", "null"]; pub const PTR_NULL_MUT: [&str; 3] = ["core", "ptr", "null_mut"]; pub const PTR_SLICE_FROM_RAW_PARTS: [&str; 3] = ["core", "ptr", "slice_from_raw_parts"]; pub const PTR_SLICE_FROM_RAW_PARTS_MUT: [&str; 3] = ["core", "ptr", "slice_from_raw_parts_mut"]; pub const PTR_SWAP_NONOVERLAPPING: [&str; 3] = ["core", "ptr", "swap_nonoverlapping"]; +pub const PTR_READ: [&str; 3] = ["core", "ptr", "read"]; +pub const PTR_READ_UNALIGNED: [&str; 3] = ["core", "ptr", "read_unaligned"]; +pub const PTR_READ_VOLATILE: [&str; 3] = ["core", "ptr", "read_volatile"]; +pub const PTR_REPLACE: [&str; 3] = ["core", "ptr", "replace"]; +pub const PTR_SWAP: [&str; 3] = ["core", "ptr", "swap"]; +pub const PTR_WRITE: [&str; 3] = ["core", "ptr", "write"]; +pub const PTR_WRITE_BYTES: [&str; 3] = ["core", "intrinsics", "write_bytes"]; +pub const PTR_WRITE_UNALIGNED: [&str; 3] = ["core", "ptr", "write_unaligned"]; +pub const PTR_WRITE_VOLATILE: [&str; 3] = ["core", "ptr", "write_volatile"]; pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"]; pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"]; @@ -117,7 +133,6 @@ pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"]; pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"]; pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"]; pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"]; -pub const REPEAT: [&str; 3] = ["core", "iter", "repeat"]; pub const RESULT: [&str; 3] = ["core", "result", "Result"]; pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"]; pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"]; @@ -131,10 +146,8 @@ pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "", "into_vec pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"]; pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"]; pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"]; -pub const STD_CONVERT_IDENTITY: [&str; 3] = ["std", "convert", "identity"]; +pub const CONVERT_IDENTITY: [&str; 3] = ["core", "convert", "identity"]; pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"]; -pub const STD_MEM_TRANSMUTE: [&str; 3] = ["std", "mem", "transmute"]; -pub const STD_PTR_NULL: [&str; 3] = ["std", "ptr", "null"]; pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"]; pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"]; pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "", "ends_with"]; diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index b52cbf31e35aa..b2ce58b597b3d 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -1,3 +1,8 @@ +// This code used to be a part of `rustc` but moved to Clippy as a result of +// https://github.com/rust-lang/rust/issues/76618. Because of that, it contains unused code and some +// of terminologies might not be relevant in the context of Clippy. Note that its behavior might +// differ from the time of `rustc` even if the name stays the same. + use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::mir::{ @@ -6,6 +11,7 @@ use rustc_middle::mir::{ }; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt}; +use rustc_semver::RustcVersion; use rustc_span::symbol::sym; use rustc_span::Span; use rustc_target::spec::abi::Abi::RustIntrinsic; @@ -13,7 +19,7 @@ use std::borrow::Cow; type McfResult = Result<(), (Span, Cow<'static, str>)>; -pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult { +pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&RustcVersion>) -> McfResult { let def_id = body.source.def_id(); let mut current = def_id; loop { @@ -70,7 +76,7 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult { )?; for bb in body.basic_blocks() { - check_terminator(tcx, body, bb.terminator())?; + check_terminator(tcx, body, bb.terminator(), msrv)?; for stmt in &bb.statements { check_statement(tcx, body, def_id, stmt)?; } @@ -268,7 +274,12 @@ fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'t Ok(()) } -fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Terminator<'tcx>) -> McfResult { +fn check_terminator( + tcx: TyCtxt<'tcx>, + body: &'a Body<'tcx>, + terminator: &Terminator<'tcx>, + msrv: Option<&RustcVersion>, +) -> McfResult { let span = terminator.source_info.span; match &terminator.kind { TerminatorKind::FalseEdge { .. } @@ -305,7 +316,7 @@ fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Termin } => { let fn_ty = func.ty(body, tcx); if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() { - if !rustc_mir::const_eval::is_min_const_fn(tcx, fn_def_id) { + if !is_const_fn(tcx, fn_def_id, msrv) { return Err(( span, format!( @@ -350,3 +361,24 @@ fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Termin TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())), } } + +fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<&RustcVersion>) -> bool { + rustc_mir::const_eval::is_const_fn(tcx, def_id) + && if let Some(const_stab) = tcx.lookup_const_stability(def_id) { + if let rustc_attr::StabilityLevel::Stable { since } = const_stab.level { + // Checking MSRV is manually necessary because `rustc` has no such concept. This entire + // function could be removed if `rustc` provided a MSRV-aware version of `is_const_fn`. + // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262. + crate::meets_msrv( + msrv, + &RustcVersion::parse(&since.as_str()) + .expect("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted"), + ) + } else { + // `rustc_mir::const_eval::is_const_fn` should return false for unstably const functions. + unreachable!(); + } + } else { + true + } +} diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 2d794d48dc5ff..53180d1f9f54f 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -66,6 +66,15 @@ pub fn indent_of(cx: &T, span: Span) -> Option { snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace())) } +/// Gets a snippet of the indentation of the line of a span +pub fn snippet_indent(cx: &T, span: Span) -> Option { + snippet_opt(cx, line_span(cx, span)).map(|mut s| { + let len = s.len() - s.trim_start().len(); + s.truncate(len); + s + }) +} + // If the snippet is empty, it's an attribute that was inserted during macro // expansion and we want to ignore those, because they could come from external // sources that the user has no control over. diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 807cfbc4c7f1f..64a80f2554fa4 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -13,7 +13,7 @@ use rustc_lint::LateContext; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TypeFoldable, UintTy}; use rustc_span::sym; -use rustc_span::symbol::Symbol; +use rustc_span::symbol::{Ident, Symbol}; use rustc_span::DUMMY_SP; use rustc_trait_selection::traits::query::normalize::AtExt; @@ -52,6 +52,25 @@ pub fn contains_adt_constructor(ty: Ty<'_>, adt: &AdtDef) -> bool { }) } +/// Resolves `::Item` for `T` +/// Do not invoke without first verifying that the type implements `Iterator` +pub fn get_iterator_item_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { + cx.tcx + .get_diagnostic_item(sym::Iterator) + .and_then(|iter_did| { + cx.tcx.associated_items(iter_did).find_by_name_and_kind( + cx.tcx, + Ident::from_str("Item"), + ty::AssocKind::Type, + iter_did, + ) + }) + .map(|assoc| { + let proj = cx.tcx.mk_projection(assoc.def_id, cx.tcx.mk_substs_trait(ty, &[])); + cx.tcx.normalize_erasing_regions(cx.param_env, proj) + }) +} + /// Returns true if ty has `iter` or `iter_mut` methods pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option { // FIXME: instead of this hard-coded list, we should check if `::iter` diff --git a/doc/adding_lints.md b/doc/adding_lints.md index 99b86953d51a6..50f0d724016ff 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -625,7 +625,7 @@ in the following steps: 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_path`, `snippet`, etc) + 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] diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index 2041aed2b97c7..bfb0c3b3f74ec 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -5,7 +5,7 @@ // When a new lint is introduced, we can search the results for new warnings and check for false // positives. -#![allow(clippy::filter_map, clippy::collapsible_else_if)] +#![allow(clippy::collapsible_else_if)] use std::ffi::OsStr; use std::process::Command; diff --git a/rust-toolchain b/rust-toolchain index 7e4d12b8632be..cd398451783d6 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-04-08" +channel = "nightly-2021-04-22" components = ["llvm-tools-preview", "rustc-dev", "rust-src"] diff --git a/tests/ui-internal/collapsible_span_lint_calls.fixed b/tests/ui-internal/collapsible_span_lint_calls.fixed index e588c23345e2f..7764cc8da7861 100644 --- a/tests/ui-internal/collapsible_span_lint_calls.fixed +++ b/tests/ui-internal/collapsible_span_lint_calls.fixed @@ -2,58 +2,18 @@ #![deny(clippy::internal)] #![feature(rustc_private)] +extern crate clippy_utils; extern crate rustc_ast; extern crate rustc_errors; extern crate rustc_lint; extern crate rustc_session; extern crate rustc_span; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then}; use rustc_ast::ast::Expr; -use rustc_errors::{Applicability, DiagnosticBuilder}; -use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; - -#[allow(unused_variables)] -pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F) -where - F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>), -{ -} - -#[allow(unused_variables)] -fn span_lint_and_help<'a, T: LintContext>( - cx: &'a T, - lint: &'static Lint, - span: Span, - msg: &str, - option_span: Option, - help: &str, -) { -} - -#[allow(unused_variables)] -fn span_lint_and_note<'a, T: LintContext>( - cx: &'a T, - lint: &'static Lint, - span: Span, - msg: &str, - note_span: Option, - note: &str, -) { -} - -#[allow(unused_variables)] -fn span_lint_and_sugg<'a, T: LintContext>( - cx: &'a T, - lint: &'static Lint, - sp: Span, - msg: &str, - help: &str, - sugg: String, - applicability: Applicability, -) { -} declare_tool_lint! { pub clippy::TEST_LINT, diff --git a/tests/ui-internal/collapsible_span_lint_calls.rs b/tests/ui-internal/collapsible_span_lint_calls.rs index d5dd3bb562b42..bdd296db8320b 100644 --- a/tests/ui-internal/collapsible_span_lint_calls.rs +++ b/tests/ui-internal/collapsible_span_lint_calls.rs @@ -2,58 +2,18 @@ #![deny(clippy::internal)] #![feature(rustc_private)] +extern crate clippy_utils; extern crate rustc_ast; extern crate rustc_errors; extern crate rustc_lint; extern crate rustc_session; extern crate rustc_span; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then}; use rustc_ast::ast::Expr; -use rustc_errors::{Applicability, DiagnosticBuilder}; -use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; - -#[allow(unused_variables)] -pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F) -where - F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>), -{ -} - -#[allow(unused_variables)] -fn span_lint_and_help<'a, T: LintContext>( - cx: &'a T, - lint: &'static Lint, - span: Span, - msg: &str, - option_span: Option, - help: &str, -) { -} - -#[allow(unused_variables)] -fn span_lint_and_note<'a, T: LintContext>( - cx: &'a T, - lint: &'static Lint, - span: Span, - msg: &str, - note_span: Option, - note: &str, -) { -} - -#[allow(unused_variables)] -fn span_lint_and_sugg<'a, T: LintContext>( - cx: &'a T, - lint: &'static Lint, - sp: Span, - msg: &str, - help: &str, - sugg: String, - applicability: Applicability, -) { -} declare_tool_lint! { pub clippy::TEST_LINT, diff --git a/tests/ui-internal/collapsible_span_lint_calls.stderr b/tests/ui-internal/collapsible_span_lint_calls.stderr index 874d4a9f255c2..0632b03857737 100644 --- a/tests/ui-internal/collapsible_span_lint_calls.stderr +++ b/tests/ui-internal/collapsible_span_lint_calls.stderr @@ -1,5 +1,5 @@ error: this call is collapsible - --> $DIR/collapsible_span_lint_calls.rs:75:9 + --> $DIR/collapsible_span_lint_calls.rs:35:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { LL | | db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable); @@ -14,7 +14,7 @@ LL | #![deny(clippy::internal)] = note: `#[deny(clippy::collapsible_span_lint_calls)]` implied by `#[deny(clippy::internal)]` error: this call is collapsible - --> $DIR/collapsible_span_lint_calls.rs:78:9 + --> $DIR/collapsible_span_lint_calls.rs:38:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { LL | | db.span_help(expr.span, help_msg); @@ -22,7 +22,7 @@ LL | | }); | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg)` error: this call is collapsible - --> $DIR/collapsible_span_lint_calls.rs:81:9 + --> $DIR/collapsible_span_lint_calls.rs:41:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { LL | | db.help(help_msg); @@ -30,7 +30,7 @@ LL | | }); | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg)` error: this call is collspible - --> $DIR/collapsible_span_lint_calls.rs:84:9 + --> $DIR/collapsible_span_lint_calls.rs:44:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { LL | | db.span_note(expr.span, note_msg); @@ -38,7 +38,7 @@ LL | | }); | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg)` error: this call is collspible - --> $DIR/collapsible_span_lint_calls.rs:87:9 + --> $DIR/collapsible_span_lint_calls.rs:47:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { LL | | db.note(note_msg); diff --git a/tests/ui-internal/match_type_on_diag_item.rs b/tests/ui-internal/match_type_on_diag_item.rs index fe950b0aa7c70..063f0c6460c5e 100644 --- a/tests/ui-internal/match_type_on_diag_item.rs +++ b/tests/ui-internal/match_type_on_diag_item.rs @@ -1,29 +1,18 @@ #![deny(clippy::internal)] #![feature(rustc_private)] +extern crate clippy_utils; extern crate rustc_hir; extern crate rustc_lint; extern crate rustc_middle; + #[macro_use] extern crate rustc_session; +use clippy_utils::{paths, ty::match_type}; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; -mod paths { - pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"]; -} - -mod utils { - use super::*; - - pub fn match_type(_cx: &LateContext<'_>, _ty: Ty<'_>, _path: &[&str]) -> bool { - false - } -} - -use utils::match_type; - declare_lint! { pub TEST_LINT, Warn, @@ -38,12 +27,12 @@ impl<'tcx> LateLintPass<'tcx> for Pass { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr) { let ty = cx.typeck_results().expr_ty(expr); - let _ = match_type(cx, ty, &paths::VEC); + let _ = match_type(cx, ty, &paths::VEC); // FIXME: Doesn't lint external paths let _ = match_type(cx, ty, &OPTION); let _ = match_type(cx, ty, &["core", "result", "Result"]); let rc_path = &["alloc", "rc", "Rc"]; - let _ = utils::match_type(cx, ty, rc_path); + let _ = clippy_utils::ty::match_type(cx, ty, rc_path); } } diff --git a/tests/ui-internal/match_type_on_diag_item.stderr b/tests/ui-internal/match_type_on_diag_item.stderr index 82465dbaf6ecc..714729605658c 100644 --- a/tests/ui-internal/match_type_on_diag_item.stderr +++ b/tests/ui-internal/match_type_on_diag_item.stderr @@ -1,8 +1,8 @@ -error: usage of `utils::match_type()` on a type diagnostic item - --> $DIR/match_type_on_diag_item.rs:41:17 +error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item + --> $DIR/match_type_on_diag_item.rs:31:17 | -LL | let _ = match_type(cx, ty, &paths::VEC); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::vec_type)` +LL | let _ = match_type(cx, ty, &OPTION); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::option_type)` | note: the lint level is defined here --> $DIR/match_type_on_diag_item.rs:1:9 @@ -11,23 +11,17 @@ LL | #![deny(clippy::internal)] | ^^^^^^^^^^^^^^^^ = note: `#[deny(clippy::match_type_on_diagnostic_item)]` implied by `#[deny(clippy::internal)]` -error: usage of `utils::match_type()` on a type diagnostic item - --> $DIR/match_type_on_diag_item.rs:42:17 - | -LL | let _ = match_type(cx, ty, &OPTION); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::option_type)` - -error: usage of `utils::match_type()` on a type diagnostic item - --> $DIR/match_type_on_diag_item.rs:43:17 +error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item + --> $DIR/match_type_on_diag_item.rs:32:17 | LL | let _ = match_type(cx, ty, &["core", "result", "Result"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::result_type)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::result_type)` -error: usage of `utils::match_type()` on a type diagnostic item - --> $DIR/match_type_on_diag_item.rs:46:17 +error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item + --> $DIR/match_type_on_diag_item.rs:35:17 | -LL | let _ = utils::match_type(cx, ty, rc_path); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::Rc)` +LL | let _ = clippy_utils::ty::match_type(cx, ty, rc_path); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::Rc)` -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/asm_syntax.rs b/tests/ui/asm_syntax.rs index 658cae397e149..4a62f6f2909f3 100644 --- a/tests/ui/asm_syntax.rs +++ b/tests/ui/asm_syntax.rs @@ -1,5 +1,7 @@ -#![feature(asm)] // only-x86_64 +// ignore-aarch64 + +#![feature(asm)] #[warn(clippy::inline_asm_x86_intel_syntax)] mod warn_intel { @@ -23,6 +25,7 @@ mod warn_att { } } +#[cfg(target_arch = "x86_64")] fn main() { unsafe { warn_att::use_asm(); diff --git a/tests/ui/asm_syntax.stderr b/tests/ui/asm_syntax.stderr index 27b51166eacb8..e3abbe086586e 100644 --- a/tests/ui/asm_syntax.stderr +++ b/tests/ui/asm_syntax.stderr @@ -1,5 +1,5 @@ error: Intel x86 assembly syntax used - --> $DIR/asm_syntax.rs:7:9 + --> $DIR/asm_syntax.rs:9:9 | LL | asm!(""); | ^^^^^^^^^ @@ -8,7 +8,7 @@ LL | asm!(""); = help: use AT&T x86 assembly syntax error: Intel x86 assembly syntax used - --> $DIR/asm_syntax.rs:8:9 + --> $DIR/asm_syntax.rs:10:9 | LL | asm!("", options()); | ^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | asm!("", options()); = help: use AT&T x86 assembly syntax error: Intel x86 assembly syntax used - --> $DIR/asm_syntax.rs:9:9 + --> $DIR/asm_syntax.rs:11:9 | LL | asm!("", options(nostack)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | asm!("", options(nostack)); = help: use AT&T x86 assembly syntax error: AT&T x86 assembly syntax used - --> $DIR/asm_syntax.rs:21:9 + --> $DIR/asm_syntax.rs:23:9 | LL | asm!("", options(att_syntax)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | asm!("", options(att_syntax)); = help: use Intel x86 assembly syntax error: AT&T x86 assembly syntax used - --> $DIR/asm_syntax.rs:22:9 + --> $DIR/asm_syntax.rs:24:9 | LL | asm!("", options(nostack, att_syntax)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/bool_assert_comparison.rs b/tests/ui/bool_assert_comparison.rs new file mode 100644 index 0000000000000..2de402fae8c7d --- /dev/null +++ b/tests/ui/bool_assert_comparison.rs @@ -0,0 +1,59 @@ +#![warn(clippy::bool_assert_comparison)] + +macro_rules! a { + () => { + true + }; +} +macro_rules! b { + () => { + true + }; +} + +fn main() { + assert_eq!("a".len(), 1); + assert_eq!("a".is_empty(), false); + assert_eq!("".is_empty(), true); + assert_eq!(true, "".is_empty()); + assert_eq!(a!(), b!()); + assert_eq!(a!(), "".is_empty()); + assert_eq!("".is_empty(), b!()); + + assert_ne!("a".len(), 1); + assert_ne!("a".is_empty(), false); + assert_ne!("".is_empty(), true); + assert_ne!(true, "".is_empty()); + assert_ne!(a!(), b!()); + assert_ne!(a!(), "".is_empty()); + assert_ne!("".is_empty(), b!()); + + debug_assert_eq!("a".len(), 1); + debug_assert_eq!("a".is_empty(), false); + debug_assert_eq!("".is_empty(), true); + debug_assert_eq!(true, "".is_empty()); + debug_assert_eq!(a!(), b!()); + debug_assert_eq!(a!(), "".is_empty()); + debug_assert_eq!("".is_empty(), b!()); + + debug_assert_ne!("a".len(), 1); + debug_assert_ne!("a".is_empty(), false); + debug_assert_ne!("".is_empty(), true); + debug_assert_ne!(true, "".is_empty()); + debug_assert_ne!(a!(), b!()); + debug_assert_ne!(a!(), "".is_empty()); + debug_assert_ne!("".is_empty(), b!()); + + // assert with error messages + assert_eq!("a".len(), 1, "tadam {}", 1); + assert_eq!("a".len(), 1, "tadam {}", true); + assert_eq!("a".is_empty(), false, "tadam {}", 1); + assert_eq!("a".is_empty(), false, "tadam {}", true); + assert_eq!(false, "a".is_empty(), "tadam {}", true); + + debug_assert_eq!("a".len(), 1, "tadam {}", 1); + debug_assert_eq!("a".len(), 1, "tadam {}", true); + debug_assert_eq!("a".is_empty(), false, "tadam {}", 1); + debug_assert_eq!("a".is_empty(), false, "tadam {}", true); + debug_assert_eq!(false, "a".is_empty(), "tadam {}", true); +} diff --git a/tests/ui/bool_assert_comparison.stderr b/tests/ui/bool_assert_comparison.stderr new file mode 100644 index 0000000000000..f57acf520d5f1 --- /dev/null +++ b/tests/ui/bool_assert_comparison.stderr @@ -0,0 +1,112 @@ +error: used `assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:16:5 + | +LL | assert_eq!("a".is_empty(), false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | + = note: `-D clippy::bool-assert-comparison` implied by `-D warnings` + +error: used `assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:17:5 + | +LL | assert_eq!("".is_empty(), true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + +error: used `assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:18:5 + | +LL | assert_eq!(true, "".is_empty()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + +error: used `assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:24:5 + | +LL | assert_ne!("a".is_empty(), false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + +error: used `assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:25:5 + | +LL | assert_ne!("".is_empty(), true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + +error: used `assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:26:5 + | +LL | assert_ne!(true, "".is_empty()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + +error: used `debug_assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:32:5 + | +LL | debug_assert_eq!("a".is_empty(), false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + +error: used `debug_assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:33:5 + | +LL | debug_assert_eq!("".is_empty(), true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + +error: used `debug_assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:34:5 + | +LL | debug_assert_eq!(true, "".is_empty()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + +error: used `debug_assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:40:5 + | +LL | debug_assert_ne!("a".is_empty(), false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + +error: used `debug_assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:41:5 + | +LL | debug_assert_ne!("".is_empty(), true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + +error: used `debug_assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:42:5 + | +LL | debug_assert_ne!(true, "".is_empty()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + +error: used `assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:50:5 + | +LL | assert_eq!("a".is_empty(), false, "tadam {}", 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + +error: used `assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:51:5 + | +LL | assert_eq!("a".is_empty(), false, "tadam {}", true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + +error: used `assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:52:5 + | +LL | assert_eq!(false, "a".is_empty(), "tadam {}", true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + +error: used `debug_assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:56:5 + | +LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + +error: used `debug_assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:57:5 + | +LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + +error: used `debug_assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:58:5 + | +LL | debug_assert_eq!(false, "a".is_empty(), "tadam {}", true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + +error: aborting due to 18 previous errors + diff --git a/tests/ui/branches_sharing_code/shared_at_bottom.rs b/tests/ui/branches_sharing_code/shared_at_bottom.rs index c389c243d447e..ce2040bdeb82d 100644 --- a/tests/ui/branches_sharing_code/shared_at_bottom.rs +++ b/tests/ui/branches_sharing_code/shared_at_bottom.rs @@ -206,4 +206,18 @@ fn fp_test() { } } +fn fp_if_let_issue7054() { + // This shouldn't trigger the lint + let string; + let _x = if let true = true { + "" + } else if true { + string = "x".to_owned(); + &string + } else { + string = "y".to_owned(); + &string + }; +} + fn main() {} diff --git a/tests/ui/branches_sharing_code/shared_at_top.rs b/tests/ui/branches_sharing_code/shared_at_top.rs index e65bcfd78737f..51a46481399b4 100644 --- a/tests/ui/branches_sharing_code/shared_at_top.rs +++ b/tests/ui/branches_sharing_code/shared_at_top.rs @@ -100,4 +100,15 @@ fn check_if_same_than_else_mask() { } } +#[allow(clippy::vec_init_then_push)] +fn pf_local_with_inferred_type_issue7053() { + if true { + let mut v = Vec::new(); + v.push(0); + } else { + let mut v = Vec::new(); + v.push(""); + }; +} + fn main() {} diff --git a/tests/ui/cloned_instead_of_copied.fixed b/tests/ui/cloned_instead_of_copied.fixed new file mode 100644 index 0000000000000..4eb999e18e64e --- /dev/null +++ b/tests/ui/cloned_instead_of_copied.fixed @@ -0,0 +1,15 @@ +// run-rustfix +#![warn(clippy::cloned_instead_of_copied)] + +fn main() { + // yay + let _ = [1].iter().copied(); + let _ = vec!["hi"].iter().copied(); + let _ = Some(&1).copied(); + let _ = Box::new([1].iter()).copied(); + let _ = Box::new(Some(&1)).copied(); + + // nay + let _ = [String::new()].iter().cloned(); + let _ = Some(&String::new()).cloned(); +} diff --git a/tests/ui/cloned_instead_of_copied.rs b/tests/ui/cloned_instead_of_copied.rs new file mode 100644 index 0000000000000..894496c0ebbb5 --- /dev/null +++ b/tests/ui/cloned_instead_of_copied.rs @@ -0,0 +1,15 @@ +// run-rustfix +#![warn(clippy::cloned_instead_of_copied)] + +fn main() { + // yay + let _ = [1].iter().cloned(); + let _ = vec!["hi"].iter().cloned(); + let _ = Some(&1).cloned(); + let _ = Box::new([1].iter()).cloned(); + let _ = Box::new(Some(&1)).cloned(); + + // nay + let _ = [String::new()].iter().cloned(); + let _ = Some(&String::new()).cloned(); +} diff --git a/tests/ui/cloned_instead_of_copied.stderr b/tests/ui/cloned_instead_of_copied.stderr new file mode 100644 index 0000000000000..e0707d3214689 --- /dev/null +++ b/tests/ui/cloned_instead_of_copied.stderr @@ -0,0 +1,34 @@ +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:6:24 + | +LL | let _ = [1].iter().cloned(); + | ^^^^^^ help: try: `copied` + | + = note: `-D clippy::cloned-instead-of-copied` implied by `-D warnings` + +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:7:31 + | +LL | let _ = vec!["hi"].iter().cloned(); + | ^^^^^^ help: try: `copied` + +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:8:22 + | +LL | let _ = Some(&1).cloned(); + | ^^^^^^ help: try: `copied` + +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:9:34 + | +LL | let _ = Box::new([1].iter()).cloned(); + | ^^^^^^ help: try: `copied` + +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:10:32 + | +LL | let _ = Box::new(Some(&1)).cloned(); + | ^^^^^^ help: try: `copied` + +error: aborting due to 5 previous errors + diff --git a/tests/ui/crashes/ice-5835.rs b/tests/ui/crashes/ice-5835.rs new file mode 100644 index 0000000000000..5e99cb432b6e2 --- /dev/null +++ b/tests/ui/crashes/ice-5835.rs @@ -0,0 +1,9 @@ +#[rustfmt::skip] +pub struct Foo { + /// 位 + /// ^ Do not remove this tab character. + /// It was required to trigger the ICE. + pub bar: u8, +} + +fn main() {} diff --git a/tests/ui/crashes/ice-5835.stderr b/tests/ui/crashes/ice-5835.stderr new file mode 100644 index 0000000000000..c972bcb60a0cd --- /dev/null +++ b/tests/ui/crashes/ice-5835.stderr @@ -0,0 +1,10 @@ +error: using tabs in doc comments is not recommended + --> $DIR/ice-5835.rs:3:10 + | +LL | /// 位 + | ^^^^ help: consider using four spaces per tab + | + = note: `-D clippy::tabs-in-doc-comments` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/deprecated.rs b/tests/ui/deprecated.rs index fc444c0bea720..dbf0b03af769c 100644 --- a/tests/ui/deprecated.rs +++ b/tests/ui/deprecated.rs @@ -11,5 +11,6 @@ #[warn(clippy::panic_params)] #[warn(clippy::unknown_clippy_lints)] #[warn(clippy::find_map)] +#[warn(clippy::filter_map)] fn main() {} diff --git a/tests/ui/deprecated.stderr b/tests/ui/deprecated.stderr index 64efcd18f8891..3e125c1dab568 100644 --- a/tests/ui/deprecated.stderr +++ b/tests/ui/deprecated.stderr @@ -24,23 +24,23 @@ error: lint `clippy::unused_collect` has been removed: `collect` has been marked LL | #[warn(clippy::unused_collect)] | ^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::invalid_ref` has been removed: superseded by rustc lint `invalid_value` +error: lint `clippy::invalid_ref` has been renamed to `invalid_value` --> $DIR/deprecated.rs:5:8 | LL | #[warn(clippy::invalid_ref)] - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` -error: lint `clippy::into_iter_on_array` has been removed: this lint has been uplifted to rustc and is now called `array_into_iter` +error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` --> $DIR/deprecated.rs:6:8 | LL | #[warn(clippy::into_iter_on_array)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` -error: lint `clippy::unused_label` has been removed: this lint has been uplifted to rustc and is now called `unused_labels` +error: lint `clippy::unused_label` has been renamed to `unused_labels` --> $DIR/deprecated.rs:7:8 | LL | #[warn(clippy::unused_label)] - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` 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 @@ -48,29 +48,29 @@ error: lint `clippy::regex_macro` has been removed: the regex! macro has been re LL | #[warn(clippy::regex_macro)] | ^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::drop_bounds` has been removed: this lint has been uplifted to rustc and is now called `drop_bounds` +error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` --> $DIR/deprecated.rs:9:8 | LL | #[warn(clippy::drop_bounds)] - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` -error: lint `clippy::temporary_cstring_as_ptr` has been removed: this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr` +error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` --> $DIR/deprecated.rs:10:8 | LL | #[warn(clippy::temporary_cstring_as_ptr)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` -error: lint `clippy::panic_params` has been removed: this lint has been uplifted to rustc and is now called `panic_fmt` +error: lint `clippy::panic_params` has been renamed to `non_fmt_panic` --> $DIR/deprecated.rs:11:8 | LL | #[warn(clippy::panic_params)] - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panic` -error: lint `clippy::unknown_clippy_lints` has been removed: this lint has been integrated into the `unknown_lints` rustc lint +error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` --> $DIR/deprecated.rs:12:8 | LL | #[warn(clippy::unknown_clippy_lints)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` 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 @@ -78,11 +78,17 @@ error: lint `clippy::find_map` has been removed: this lint has been replaced by 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 + | +LL | #[warn(clippy::filter_map)] + | ^^^^^^^^^^^^^^^^^^ + error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` has been stabilized in 1.7 --> $DIR/deprecated.rs:1:8 | LL | #[warn(clippy::unstable_as_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 14 previous errors +error: aborting due to 15 previous errors diff --git a/tests/ui/entry.fixed b/tests/ui/entry.fixed new file mode 100644 index 0000000000000..cfad3090ba38d --- /dev/null +++ b/tests/ui/entry.fixed @@ -0,0 +1,155 @@ +// run-rustfix + +#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)] +#![warn(clippy::map_entry)] +#![feature(asm)] + +use std::collections::{BTreeMap, HashMap}; +use std::hash::Hash; + +macro_rules! m { + ($e:expr) => {{ $e }}; +} + +macro_rules! insert { + ($map:expr, $key:expr, $val:expr) => { + $map.insert($key, $val) + }; +} + +fn foo() {} + +fn hash_map(m: &mut HashMap, m2: &mut HashMap, k: K, k2: K, v: V, v2: V) { + // or_insert(v) + m.entry(k).or_insert(v); + + // semicolon on insert, use or_insert_with(..) + m.entry(k).or_insert_with(|| { + if true { + v + } else { + v2 + } + }); + + // semicolon on if, use or_insert_with(..) + m.entry(k).or_insert_with(|| { + if true { + v + } else { + v2 + } + }); + + // early return, use if let + if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { + if true { + e.insert(v); + } else { + e.insert(v2); + return; + } + } + + // use or_insert_with(..) + m.entry(k).or_insert_with(|| { + foo(); + v + }); + + // semicolon on insert and match, use or_insert_with(..) + m.entry(k).or_insert_with(|| { + match 0 { + 1 if true => { + v + }, + _ => { + v2 + }, + } + }); + + // one branch doesn't insert, use if let + if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { + match 0 { + 0 => foo(), + _ => { + e.insert(v2); + }, + }; + } + + // use or_insert_with + m.entry(k).or_insert_with(|| { + foo(); + match 0 { + 0 if false => { + v + }, + 1 => { + foo(); + v + }, + 2 | 3 => { + for _ in 0..2 { + foo(); + } + if true { + v + } else { + v2 + } + }, + _ => { + v2 + }, + } + }); + + // ok, insert in loop + if !m.contains_key(&k) { + for _ in 0..2 { + m.insert(k, v); + } + } + + // macro_expansion test, use or_insert(..) + m.entry(m!(k)).or_insert_with(|| m!(v)); + + // ok, map used before insertion + if !m.contains_key(&k) { + let _ = m.len(); + m.insert(k, v); + } + + // ok, inline asm + if !m.contains_key(&k) { + unsafe { asm!("nop") } + m.insert(k, v); + } + + // ok, different keys. + if !m.contains_key(&k) { + m.insert(k2, v); + } + + // ok, different maps + if !m.contains_key(&k) { + m2.insert(k, v); + } + + // ok, insert in macro + if !m.contains_key(&k) { + insert!(m, k, v); + } +} + +fn btree_map(m: &mut BTreeMap, k: K, v: V, v2: V) { + // insert then do something, use if let + if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) { + e.insert(v); + foo(); + } +} + +fn main() {} diff --git a/tests/ui/entry.rs b/tests/ui/entry.rs new file mode 100644 index 0000000000000..fa9280b58de11 --- /dev/null +++ b/tests/ui/entry.rs @@ -0,0 +1,159 @@ +// run-rustfix + +#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)] +#![warn(clippy::map_entry)] +#![feature(asm)] + +use std::collections::{BTreeMap, HashMap}; +use std::hash::Hash; + +macro_rules! m { + ($e:expr) => {{ $e }}; +} + +macro_rules! insert { + ($map:expr, $key:expr, $val:expr) => { + $map.insert($key, $val) + }; +} + +fn foo() {} + +fn hash_map(m: &mut HashMap, m2: &mut HashMap, k: K, k2: K, v: V, v2: V) { + // or_insert(v) + if !m.contains_key(&k) { + m.insert(k, v); + } + + // semicolon on insert, use or_insert_with(..) + if !m.contains_key(&k) { + if true { + m.insert(k, v); + } else { + m.insert(k, v2); + } + } + + // semicolon on if, use or_insert_with(..) + if !m.contains_key(&k) { + if true { + m.insert(k, v) + } else { + m.insert(k, v2) + }; + } + + // early return, use if let + if !m.contains_key(&k) { + if true { + m.insert(k, v); + } else { + m.insert(k, v2); + return; + } + } + + // use or_insert_with(..) + if !m.contains_key(&k) { + foo(); + m.insert(k, v); + } + + // semicolon on insert and match, use or_insert_with(..) + if !m.contains_key(&k) { + match 0 { + 1 if true => { + m.insert(k, v); + }, + _ => { + m.insert(k, v2); + }, + }; + } + + // one branch doesn't insert, use if let + if !m.contains_key(&k) { + match 0 { + 0 => foo(), + _ => { + m.insert(k, v2); + }, + }; + } + + // use or_insert_with + if !m.contains_key(&k) { + foo(); + match 0 { + 0 if false => { + m.insert(k, v); + }, + 1 => { + foo(); + m.insert(k, v); + }, + 2 | 3 => { + for _ in 0..2 { + foo(); + } + if true { + m.insert(k, v); + } else { + m.insert(k, v2); + }; + }, + _ => { + m.insert(k, v2); + }, + } + } + + // ok, insert in loop + if !m.contains_key(&k) { + for _ in 0..2 { + m.insert(k, v); + } + } + + // macro_expansion test, use or_insert(..) + if !m.contains_key(&m!(k)) { + m.insert(m!(k), m!(v)); + } + + // ok, map used before insertion + if !m.contains_key(&k) { + let _ = m.len(); + m.insert(k, v); + } + + // ok, inline asm + if !m.contains_key(&k) { + unsafe { asm!("nop") } + m.insert(k, v); + } + + // ok, different keys. + if !m.contains_key(&k) { + m.insert(k2, v); + } + + // ok, different maps + if !m.contains_key(&k) { + m2.insert(k, v); + } + + // ok, insert in macro + if !m.contains_key(&k) { + insert!(m, k, v); + } +} + +fn btree_map(m: &mut BTreeMap, k: K, v: V, v2: V) { + // insert then do something, use if let + if !m.contains_key(&k) { + m.insert(k, v); + foo(); + } +} + +fn main() {} diff --git a/tests/ui/entry.stderr b/tests/ui/entry.stderr new file mode 100644 index 0000000000000..2f075a97010a6 --- /dev/null +++ b/tests/ui/entry.stderr @@ -0,0 +1,186 @@ +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry.rs:24:5 + | +LL | / if !m.contains_key(&k) { +LL | | m.insert(k, v); +LL | | } + | |_____^ help: try this: `m.entry(k).or_insert(v);` + | + = note: `-D clippy::map-entry` implied by `-D warnings` + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry.rs:29:5 + | +LL | / if !m.contains_key(&k) { +LL | | if true { +LL | | m.insert(k, v); +LL | | } else { +LL | | m.insert(k, v2); +LL | | } +LL | | } + | |_____^ + | +help: try this + | +LL | m.entry(k).or_insert_with(|| { +LL | if true { +LL | v +LL | } else { +LL | v2 +LL | } + ... + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry.rs:38:5 + | +LL | / if !m.contains_key(&k) { +LL | | if true { +LL | | m.insert(k, v) +LL | | } else { +LL | | m.insert(k, v2) +LL | | }; +LL | | } + | |_____^ + | +help: try this + | +LL | m.entry(k).or_insert_with(|| { +LL | if true { +LL | v +LL | } else { +LL | v2 +LL | } + ... + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry.rs:47:5 + | +LL | / if !m.contains_key(&k) { +LL | | if true { +LL | | m.insert(k, v); +LL | | } else { +... | +LL | | } +LL | | } + | |_____^ + | +help: try this + | +LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { +LL | if true { +LL | e.insert(v); +LL | } else { +LL | e.insert(v2); +LL | return; + ... + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry.rs:57:5 + | +LL | / if !m.contains_key(&k) { +LL | | foo(); +LL | | m.insert(k, v); +LL | | } + | |_____^ + | +help: try this + | +LL | m.entry(k).or_insert_with(|| { +LL | foo(); +LL | v +LL | }); + | + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry.rs:63:5 + | +LL | / if !m.contains_key(&k) { +LL | | match 0 { +LL | | 1 if true => { +LL | | m.insert(k, v); +... | +LL | | }; +LL | | } + | |_____^ + | +help: try this + | +LL | m.entry(k).or_insert_with(|| { +LL | match 0 { +LL | 1 if true => { +LL | v +LL | }, +LL | _ => { + ... + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry.rs:75:5 + | +LL | / if !m.contains_key(&k) { +LL | | match 0 { +LL | | 0 => foo(), +LL | | _ => { +... | +LL | | }; +LL | | } + | |_____^ + | +help: try this + | +LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { +LL | match 0 { +LL | 0 => foo(), +LL | _ => { +LL | e.insert(v2); +LL | }, + ... + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry.rs:85:5 + | +LL | / if !m.contains_key(&k) { +LL | | foo(); +LL | | match 0 { +LL | | 0 if false => { +... | +LL | | } +LL | | } + | |_____^ + | +help: try this + | +LL | m.entry(k).or_insert_with(|| { +LL | foo(); +LL | match 0 { +LL | 0 if false => { +LL | v +LL | }, + ... + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry.rs:119:5 + | +LL | / if !m.contains_key(&m!(k)) { +LL | | m.insert(m!(k), m!(v)); +LL | | } + | |_____^ help: try this: `m.entry(m!(k)).or_insert_with(|| m!(v));` + +error: usage of `contains_key` followed by `insert` on a `BTreeMap` + --> $DIR/entry.rs:153:5 + | +LL | / if !m.contains_key(&k) { +LL | | m.insert(k, v); +LL | | foo(); +LL | | } + | |_____^ + | +help: try this + | +LL | if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) { +LL | e.insert(v); +LL | foo(); +LL | } + | + +error: aborting due to 10 previous errors + diff --git a/tests/ui/entry_fixable.fixed b/tests/ui/entry_fixable.fixed deleted file mode 100644 index dcdaae7e72430..0000000000000 --- a/tests/ui/entry_fixable.fixed +++ /dev/null @@ -1,15 +0,0 @@ -// run-rustfix - -#![allow(unused, clippy::needless_pass_by_value)] -#![warn(clippy::map_entry)] - -use std::collections::{BTreeMap, HashMap}; -use std::hash::Hash; - -fn foo() {} - -fn insert_if_absent0(m: &mut HashMap, k: K, v: V) { - m.entry(k).or_insert(v); -} - -fn main() {} diff --git a/tests/ui/entry_fixable.rs b/tests/ui/entry_fixable.rs deleted file mode 100644 index 55d5b21568d0e..0000000000000 --- a/tests/ui/entry_fixable.rs +++ /dev/null @@ -1,17 +0,0 @@ -// run-rustfix - -#![allow(unused, clippy::needless_pass_by_value)] -#![warn(clippy::map_entry)] - -use std::collections::{BTreeMap, HashMap}; -use std::hash::Hash; - -fn foo() {} - -fn insert_if_absent0(m: &mut HashMap, k: K, v: V) { - if !m.contains_key(&k) { - m.insert(k, v); - } -} - -fn main() {} diff --git a/tests/ui/entry_fixable.stderr b/tests/ui/entry_fixable.stderr deleted file mode 100644 index 87403200ced50..0000000000000 --- a/tests/ui/entry_fixable.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry_fixable.rs:12:5 - | -LL | / if !m.contains_key(&k) { -LL | | m.insert(k, v); -LL | | } - | |_____^ help: consider using: `m.entry(k).or_insert(v);` - | - = note: `-D clippy::map-entry` implied by `-D warnings` - -error: aborting due to previous error - diff --git a/tests/ui/entry_unfixable.rs b/tests/ui/entry_unfixable.rs deleted file mode 100644 index f530fc023cfbf..0000000000000 --- a/tests/ui/entry_unfixable.rs +++ /dev/null @@ -1,73 +0,0 @@ -#![allow(unused, clippy::needless_pass_by_value)] -#![warn(clippy::map_entry)] - -use std::collections::{BTreeMap, HashMap}; -use std::hash::Hash; - -fn foo() {} - -fn insert_if_absent2(m: &mut HashMap, k: K, v: V) { - if !m.contains_key(&k) { - m.insert(k, v) - } else { - None - }; -} - -fn insert_if_present2(m: &mut HashMap, k: K, v: V) { - if m.contains_key(&k) { - None - } else { - m.insert(k, v) - }; -} - -fn insert_if_absent3(m: &mut HashMap, k: K, v: V) { - if !m.contains_key(&k) { - foo(); - m.insert(k, v) - } else { - None - }; -} - -fn insert_if_present3(m: &mut HashMap, k: K, v: V) { - if m.contains_key(&k) { - None - } else { - foo(); - m.insert(k, v) - }; -} - -fn insert_in_btreemap(m: &mut BTreeMap, k: K, v: V) { - if !m.contains_key(&k) { - foo(); - m.insert(k, v) - } else { - None - }; -} - -// should not trigger -fn insert_other_if_absent(m: &mut HashMap, k: K, o: K, v: V) { - if !m.contains_key(&k) { - m.insert(o, v); - } -} - -// should not trigger, because the one uses different HashMap from another one -fn insert_from_different_map(m: HashMap, n: &mut HashMap, k: K, v: V) { - if !m.contains_key(&k) { - n.insert(k, v); - } -} - -// should not trigger, because the one uses different HashMap from another one -fn insert_from_different_map2(m: &mut HashMap, n: &mut HashMap, k: K, v: V) { - if !m.contains_key(&k) { - n.insert(k, v); - } -} - -fn main() {} diff --git a/tests/ui/entry_unfixable.stderr b/tests/ui/entry_unfixable.stderr deleted file mode 100644 index e58c8d22dc45e..0000000000000 --- a/tests/ui/entry_unfixable.stderr +++ /dev/null @@ -1,57 +0,0 @@ -error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry_unfixable.rs:10:5 - | -LL | / if !m.contains_key(&k) { -LL | | m.insert(k, v) -LL | | } else { -LL | | None -LL | | }; - | |_____^ consider using `m.entry(k)` - | - = note: `-D clippy::map-entry` implied by `-D warnings` - -error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry_unfixable.rs:18:5 - | -LL | / if m.contains_key(&k) { -LL | | None -LL | | } else { -LL | | m.insert(k, v) -LL | | }; - | |_____^ consider using `m.entry(k)` - -error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry_unfixable.rs:26:5 - | -LL | / if !m.contains_key(&k) { -LL | | foo(); -LL | | m.insert(k, v) -LL | | } else { -LL | | None -LL | | }; - | |_____^ consider using `m.entry(k)` - -error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry_unfixable.rs:35:5 - | -LL | / if m.contains_key(&k) { -LL | | None -LL | | } else { -LL | | foo(); -LL | | m.insert(k, v) -LL | | }; - | |_____^ consider using `m.entry(k)` - -error: usage of `contains_key` followed by `insert` on a `BTreeMap` - --> $DIR/entry_unfixable.rs:44:5 - | -LL | / if !m.contains_key(&k) { -LL | | foo(); -LL | | m.insert(k, v) -LL | | } else { -LL | | None -LL | | }; - | |_____^ consider using `m.entry(k)` - -error: aborting due to 5 previous errors - diff --git a/tests/ui/entry_with_else.fixed b/tests/ui/entry_with_else.fixed new file mode 100644 index 0000000000000..2332fa6313ff3 --- /dev/null +++ b/tests/ui/entry_with_else.fixed @@ -0,0 +1,73 @@ +// run-rustfix + +#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)] +#![warn(clippy::map_entry)] + +use std::collections::{BTreeMap, HashMap}; +use std::hash::Hash; + +macro_rules! m { + ($e:expr) => {{ $e }}; +} + +fn foo() {} + +fn insert_if_absent0(m: &mut HashMap, k: K, v: V, v2: V) { + match m.entry(k) { + std::collections::hash_map::Entry::Vacant(e) => { + e.insert(v); + } + std::collections::hash_map::Entry::Occupied(mut e) => { + e.insert(v2); + } + } + + match m.entry(k) { + std::collections::hash_map::Entry::Occupied(mut e) => { + e.insert(v); + } + std::collections::hash_map::Entry::Vacant(e) => { + e.insert(v2); + } + } + + if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { + e.insert(v); + } else { + foo(); + } + + if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) { + e.insert(v); + } else { + foo(); + } + + match m.entry(k) { + std::collections::hash_map::Entry::Vacant(e) => { + e.insert(v); + } + std::collections::hash_map::Entry::Occupied(mut e) => { + e.insert(v2); + } + } + + match m.entry(k) { + std::collections::hash_map::Entry::Occupied(mut e) => { + if true { Some(e.insert(v)) } else { Some(e.insert(v2)) } + } + std::collections::hash_map::Entry::Vacant(e) => { + e.insert(v); + None + } + }; + + if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) { + foo(); + Some(e.insert(v)) + } else { + None + }; +} + +fn main() {} diff --git a/tests/ui/entry_with_else.rs b/tests/ui/entry_with_else.rs new file mode 100644 index 0000000000000..2ff0c038efe27 --- /dev/null +++ b/tests/ui/entry_with_else.rs @@ -0,0 +1,60 @@ +// run-rustfix + +#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)] +#![warn(clippy::map_entry)] + +use std::collections::{BTreeMap, HashMap}; +use std::hash::Hash; + +macro_rules! m { + ($e:expr) => {{ $e }}; +} + +fn foo() {} + +fn insert_if_absent0(m: &mut HashMap, k: K, v: V, v2: V) { + if !m.contains_key(&k) { + m.insert(k, v); + } else { + m.insert(k, v2); + } + + if m.contains_key(&k) { + m.insert(k, v); + } else { + m.insert(k, v2); + } + + if !m.contains_key(&k) { + m.insert(k, v); + } else { + foo(); + } + + if !m.contains_key(&k) { + foo(); + } else { + m.insert(k, v); + } + + if !m.contains_key(&k) { + m.insert(k, v); + } else { + m.insert(k, v2); + } + + if m.contains_key(&k) { + if true { m.insert(k, v) } else { m.insert(k, v2) } + } else { + m.insert(k, v) + }; + + if m.contains_key(&k) { + foo(); + m.insert(k, v) + } else { + None + }; +} + +fn main() {} diff --git a/tests/ui/entry_with_else.stderr b/tests/ui/entry_with_else.stderr new file mode 100644 index 0000000000000..6f62ff8d37457 --- /dev/null +++ b/tests/ui/entry_with_else.stderr @@ -0,0 +1,142 @@ +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry_with_else.rs:16:5 + | +LL | / if !m.contains_key(&k) { +LL | | m.insert(k, v); +LL | | } else { +LL | | m.insert(k, v2); +LL | | } + | |_____^ + | + = note: `-D clippy::map-entry` implied by `-D warnings` +help: try this + | +LL | match m.entry(k) { +LL | std::collections::hash_map::Entry::Vacant(e) => { +LL | e.insert(v); +LL | } +LL | std::collections::hash_map::Entry::Occupied(mut e) => { +LL | e.insert(v2); + ... + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry_with_else.rs:22:5 + | +LL | / if m.contains_key(&k) { +LL | | m.insert(k, v); +LL | | } else { +LL | | m.insert(k, v2); +LL | | } + | |_____^ + | +help: try this + | +LL | match m.entry(k) { +LL | std::collections::hash_map::Entry::Occupied(mut e) => { +LL | e.insert(v); +LL | } +LL | std::collections::hash_map::Entry::Vacant(e) => { +LL | e.insert(v2); + ... + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry_with_else.rs:28:5 + | +LL | / if !m.contains_key(&k) { +LL | | m.insert(k, v); +LL | | } else { +LL | | foo(); +LL | | } + | |_____^ + | +help: try this + | +LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { +LL | e.insert(v); +LL | } else { +LL | foo(); +LL | } + | + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry_with_else.rs:34:5 + | +LL | / if !m.contains_key(&k) { +LL | | foo(); +LL | | } else { +LL | | m.insert(k, v); +LL | | } + | |_____^ + | +help: try this + | +LL | if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) { +LL | e.insert(v); +LL | } else { +LL | foo(); +LL | } + | + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry_with_else.rs:40:5 + | +LL | / if !m.contains_key(&k) { +LL | | m.insert(k, v); +LL | | } else { +LL | | m.insert(k, v2); +LL | | } + | |_____^ + | +help: try this + | +LL | match m.entry(k) { +LL | std::collections::hash_map::Entry::Vacant(e) => { +LL | e.insert(v); +LL | } +LL | std::collections::hash_map::Entry::Occupied(mut e) => { +LL | e.insert(v2); + ... + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry_with_else.rs:46:5 + | +LL | / if m.contains_key(&k) { +LL | | if true { m.insert(k, v) } else { m.insert(k, v2) } +LL | | } else { +LL | | m.insert(k, v) +LL | | }; + | |_____^ + | +help: try this + | +LL | match m.entry(k) { +LL | std::collections::hash_map::Entry::Occupied(mut e) => { +LL | if true { Some(e.insert(v)) } else { Some(e.insert(v2)) } +LL | } +LL | std::collections::hash_map::Entry::Vacant(e) => { +LL | e.insert(v); + ... + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry_with_else.rs:52:5 + | +LL | / if m.contains_key(&k) { +LL | | foo(); +LL | | m.insert(k, v) +LL | | } else { +LL | | None +LL | | }; + | |_____^ + | +help: try this + | +LL | if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) { +LL | foo(); +LL | Some(e.insert(v)) +LL | } else { +LL | None +LL | }; + | + +error: aborting due to 7 previous errors + diff --git a/tests/ui/filter_methods.rs b/tests/ui/filter_methods.rs deleted file mode 100644 index 96121b114ce6c..0000000000000 --- a/tests/ui/filter_methods.rs +++ /dev/null @@ -1,25 +0,0 @@ -#![warn(clippy::all, clippy::pedantic)] -#![allow(clippy::let_underscore_drop)] -#![allow(clippy::missing_docs_in_private_items)] - -fn main() { - let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * 2).collect(); - - let _: Vec<_> = vec![5_i8; 6] - .into_iter() - .filter(|&x| x == 0) - .flat_map(|x| x.checked_mul(2)) - .collect(); - - let _: Vec<_> = vec![5_i8; 6] - .into_iter() - .filter_map(|x| x.checked_mul(2)) - .flat_map(|x| x.checked_mul(2)) - .collect(); - - let _: Vec<_> = vec![5_i8; 6] - .into_iter() - .filter_map(|x| x.checked_mul(2)) - .map(|x| x.checked_mul(2)) - .collect(); -} diff --git a/tests/ui/filter_methods.stderr b/tests/ui/filter_methods.stderr deleted file mode 100644 index c7b4f28be3a44..0000000000000 --- a/tests/ui/filter_methods.stderr +++ /dev/null @@ -1,39 +0,0 @@ -error: called `filter(..).flat_map(..)` on an `Iterator` - --> $DIR/filter_methods.rs:8:21 - | -LL | let _: Vec<_> = vec![5_i8; 6] - | _____________________^ -LL | | .into_iter() -LL | | .filter(|&x| x == 0) -LL | | .flat_map(|x| x.checked_mul(2)) - | |_______________________________________^ - | - = note: `-D clippy::filter-map` implied by `-D warnings` - = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()` - -error: called `filter_map(..).flat_map(..)` on an `Iterator` - --> $DIR/filter_methods.rs:14:21 - | -LL | let _: Vec<_> = vec![5_i8; 6] - | _____________________^ -LL | | .into_iter() -LL | | .filter_map(|x| x.checked_mul(2)) -LL | | .flat_map(|x| x.checked_mul(2)) - | |_______________________________________^ - | - = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()` - -error: called `filter_map(..).map(..)` on an `Iterator` - --> $DIR/filter_methods.rs:20:21 - | -LL | let _: Vec<_> = vec![5_i8; 6] - | _____________________^ -LL | | .into_iter() -LL | | .filter_map(|x| x.checked_mul(2)) -LL | | .map(|x| x.checked_mul(2)) - | |__________________________________^ - | - = help: this is more succinctly expressed by only calling `.filter_map(..)` instead - -error: aborting due to 3 previous errors - diff --git a/tests/ui/flat_map_option.fixed b/tests/ui/flat_map_option.fixed new file mode 100644 index 0000000000000..6a34f008995cd --- /dev/null +++ b/tests/ui/flat_map_option.fixed @@ -0,0 +1,13 @@ +// run-rustfix +#![warn(clippy::flat_map_option)] +#![allow(clippy::redundant_closure, clippy::unnecessary_filter_map)] + +fn main() { + // yay + let c = |x| Some(x); + let _ = [1].iter().filter_map(c); + let _ = [1].iter().filter_map(Some); + + // nay + let _ = [1].iter().flat_map(|_| &Some(1)); +} diff --git a/tests/ui/flat_map_option.rs b/tests/ui/flat_map_option.rs new file mode 100644 index 0000000000000..2479abddbf04e --- /dev/null +++ b/tests/ui/flat_map_option.rs @@ -0,0 +1,13 @@ +// run-rustfix +#![warn(clippy::flat_map_option)] +#![allow(clippy::redundant_closure, clippy::unnecessary_filter_map)] + +fn main() { + // yay + let c = |x| Some(x); + let _ = [1].iter().flat_map(c); + let _ = [1].iter().flat_map(Some); + + // nay + let _ = [1].iter().flat_map(|_| &Some(1)); +} diff --git a/tests/ui/flat_map_option.stderr b/tests/ui/flat_map_option.stderr new file mode 100644 index 0000000000000..a9d8056dee978 --- /dev/null +++ b/tests/ui/flat_map_option.stderr @@ -0,0 +1,16 @@ +error: used `flat_map` where `filter_map` could be used instead + --> $DIR/flat_map_option.rs:8:24 + | +LL | let _ = [1].iter().flat_map(c); + | ^^^^^^^^ help: try: `filter_map` + | + = note: `-D clippy::flat-map-option` implied by `-D warnings` + +error: used `flat_map` where `filter_map` could be used instead + --> $DIR/flat_map_option.rs:9:24 + | +LL | let _ = [1].iter().flat_map(Some); + | ^^^^^^^^ help: try: `filter_map` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/for_loop_fixable.fixed b/tests/ui/for_loop_fixable.fixed index 249a88a0b3982..f44928d408386 100644 --- a/tests/ui/for_loop_fixable.fixed +++ b/tests/ui/for_loop_fixable.fixed @@ -281,3 +281,29 @@ mod issue_4958 { for _ in rr.into_iter() {} } } + +// explicit_into_iter_loop +#[warn(clippy::explicit_into_iter_loop)] +mod issue_6900 { + struct S; + impl S { + #[allow(clippy::should_implement_trait)] + pub fn into_iter(self) -> I { + unimplemented!() + } + } + + struct I(T); + impl Iterator for I { + type Item = T; + fn next(&mut self) -> Option { + unimplemented!() + } + } + + fn f() { + for _ in S.into_iter::() { + unimplemented!() + } + } +} diff --git a/tests/ui/for_loop_fixable.rs b/tests/ui/for_loop_fixable.rs index 306d85a6351e1..5b1eb3ee4dcd2 100644 --- a/tests/ui/for_loop_fixable.rs +++ b/tests/ui/for_loop_fixable.rs @@ -281,3 +281,29 @@ mod issue_4958 { for _ in rr.into_iter() {} } } + +// explicit_into_iter_loop +#[warn(clippy::explicit_into_iter_loop)] +mod issue_6900 { + struct S; + impl S { + #[allow(clippy::should_implement_trait)] + pub fn into_iter(self) -> I { + unimplemented!() + } + } + + struct I(T); + impl Iterator for I { + type Item = T; + fn next(&mut self) -> Option { + unimplemented!() + } + } + + fn f() { + for _ in S.into_iter::() { + unimplemented!() + } + } +} diff --git a/tests/ui/format.fixed b/tests/ui/format.fixed index 740a22a07d747..e4cfb005fd1d0 100644 --- a/tests/ui/format.fixed +++ b/tests/ui/format.fixed @@ -65,4 +65,8 @@ fn main() { // False positive let a = "foo".to_string(); let _ = Some(a + "bar"); + + // Wrap it with braces + let v: Vec = vec!["foo".to_string(), "bar".to_string()]; + let _s: String = (&*v.join("\n")).to_string(); } diff --git a/tests/ui/format.rs b/tests/ui/format.rs index b604d79cca373..683957f0ff0f7 100644 --- a/tests/ui/format.rs +++ b/tests/ui/format.rs @@ -67,4 +67,8 @@ fn main() { // False positive let a = "foo".to_string(); let _ = Some(format!("{}", a + "bar")); + + // Wrap it with braces + let v: Vec = vec!["foo".to_string(), "bar".to_string()]; + let _s: String = format!("{}", &*v.join("\n")); } diff --git a/tests/ui/format.stderr b/tests/ui/format.stderr index 96df7f37f7792..2017eb2b3838f 100644 --- a/tests/ui/format.stderr +++ b/tests/ui/format.stderr @@ -87,5 +87,11 @@ error: useless use of `format!` LL | let _ = Some(format!("{}", a + "bar")); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"` -error: aborting due to 13 previous errors +error: useless use of `format!` + --> $DIR/format.rs:73:22 + | +LL | let _s: String = format!("{}", &*v.join("/n")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `(&*v.join("/n")).to_string()` + +error: aborting due to 14 previous errors diff --git a/tests/ui/from_over_into.stderr b/tests/ui/from_over_into.stderr index b101d2704fbda..2951e6bdac430 100644 --- a/tests/ui/from_over_into.stderr +++ b/tests/ui/from_over_into.stderr @@ -5,7 +5,7 @@ LL | impl Into for String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::from-over-into` implied by `-D warnings` - = help: consider to implement `From` instead + = help: consider to implement `From` instead error: aborting due to previous error diff --git a/tests/ui/inconsistent_struct_constructor.fixed b/tests/ui/inconsistent_struct_constructor.fixed index 8d9c311003508..d1025743790a9 100644 --- a/tests/ui/inconsistent_struct_constructor.fixed +++ b/tests/ui/inconsistent_struct_constructor.fixed @@ -13,6 +13,15 @@ struct Foo { 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; @@ -24,6 +33,10 @@ mod without_base { // 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 }; diff --git a/tests/ui/inconsistent_struct_constructor.rs b/tests/ui/inconsistent_struct_constructor.rs index 63fac9105015d..b095aa64a2174 100644 --- a/tests/ui/inconsistent_struct_constructor.rs +++ b/tests/ui/inconsistent_struct_constructor.rs @@ -13,6 +13,15 @@ struct Foo { 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; @@ -24,6 +33,10 @@ mod without_base { // 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 }; diff --git a/tests/ui/inconsistent_struct_constructor.stderr b/tests/ui/inconsistent_struct_constructor.stderr index d021bb19579f4..ef308dedb1661 100644 --- a/tests/ui/inconsistent_struct_constructor.stderr +++ b/tests/ui/inconsistent_struct_constructor.stderr @@ -1,5 +1,5 @@ error: struct constructor field order is inconsistent with struct definition field order - --> $DIR/inconsistent_struct_constructor.rs:25:9 + --> $DIR/inconsistent_struct_constructor.rs:34:9 | LL | Foo { y, x, z }; | ^^^^^^^^^^^^^^^ help: try: `Foo { x, y, z }` @@ -7,7 +7,7 @@ LL | Foo { y, x, 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:43:9 + --> $DIR/inconsistent_struct_constructor.rs:56:9 | LL | / Foo { LL | | z, diff --git a/tests/ui/invalid_null_ptr_usage.fixed b/tests/ui/invalid_null_ptr_usage.fixed new file mode 100644 index 0000000000000..4f5322ebf202f --- /dev/null +++ b/tests/ui/invalid_null_ptr_usage.fixed @@ -0,0 +1,49 @@ +// run-rustfix + +fn main() { + unsafe { + let _slice: &[usize] = std::slice::from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0); + let _slice: &[usize] = std::slice::from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0); + + let _slice: &[usize] = std::slice::from_raw_parts_mut(core::ptr::NonNull::dangling().as_ptr(), 0); + + std::ptr::copy::(core::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0); + std::ptr::copy::(std::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0); + + std::ptr::copy_nonoverlapping::(core::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0); + std::ptr::copy_nonoverlapping::(std::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0); + + struct A; // zero sized struct + assert_eq!(std::mem::size_of::(), 0); + + let _a: A = std::ptr::read(core::ptr::NonNull::dangling().as_ptr()); + let _a: A = std::ptr::read(core::ptr::NonNull::dangling().as_ptr()); + + let _a: A = std::ptr::read_unaligned(core::ptr::NonNull::dangling().as_ptr()); + let _a: A = std::ptr::read_unaligned(core::ptr::NonNull::dangling().as_ptr()); + + let _a: A = std::ptr::read_volatile(core::ptr::NonNull::dangling().as_ptr()); + let _a: A = std::ptr::read_volatile(core::ptr::NonNull::dangling().as_ptr()); + + let _a: A = std::ptr::replace(core::ptr::NonNull::dangling().as_ptr(), A); + + let _slice: *const [usize] = std::ptr::slice_from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0); + let _slice: *const [usize] = std::ptr::slice_from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0); + + let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(core::ptr::NonNull::dangling().as_ptr(), 0); + + std::ptr::swap::(core::ptr::NonNull::dangling().as_ptr(), &mut A); + std::ptr::swap::(&mut A, core::ptr::NonNull::dangling().as_ptr()); + + std::ptr::swap_nonoverlapping::(core::ptr::NonNull::dangling().as_ptr(), &mut A, 0); + std::ptr::swap_nonoverlapping::(&mut A, core::ptr::NonNull::dangling().as_ptr(), 0); + + std::ptr::write(core::ptr::NonNull::dangling().as_ptr(), A); + + std::ptr::write_unaligned(core::ptr::NonNull::dangling().as_ptr(), A); + + std::ptr::write_volatile(core::ptr::NonNull::dangling().as_ptr(), A); + + std::ptr::write_bytes::(core::ptr::NonNull::dangling().as_ptr(), 42, 0); + } +} diff --git a/tests/ui/invalid_null_ptr_usage.rs b/tests/ui/invalid_null_ptr_usage.rs new file mode 100644 index 0000000000000..ae51c52d8af0c --- /dev/null +++ b/tests/ui/invalid_null_ptr_usage.rs @@ -0,0 +1,49 @@ +// run-rustfix + +fn main() { + unsafe { + let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null(), 0); + let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null_mut(), 0); + + let _slice: &[usize] = std::slice::from_raw_parts_mut(std::ptr::null_mut(), 0); + + std::ptr::copy::(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0); + std::ptr::copy::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0); + + std::ptr::copy_nonoverlapping::(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0); + std::ptr::copy_nonoverlapping::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0); + + struct A; // zero sized struct + assert_eq!(std::mem::size_of::(), 0); + + let _a: A = std::ptr::read(std::ptr::null()); + let _a: A = std::ptr::read(std::ptr::null_mut()); + + let _a: A = std::ptr::read_unaligned(std::ptr::null()); + let _a: A = std::ptr::read_unaligned(std::ptr::null_mut()); + + let _a: A = std::ptr::read_volatile(std::ptr::null()); + let _a: A = std::ptr::read_volatile(std::ptr::null_mut()); + + let _a: A = std::ptr::replace(std::ptr::null_mut(), A); + + let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null(), 0); + let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null_mut(), 0); + + let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(std::ptr::null_mut(), 0); + + std::ptr::swap::(std::ptr::null_mut(), &mut A); + std::ptr::swap::(&mut A, std::ptr::null_mut()); + + std::ptr::swap_nonoverlapping::(std::ptr::null_mut(), &mut A, 0); + std::ptr::swap_nonoverlapping::(&mut A, std::ptr::null_mut(), 0); + + std::ptr::write(std::ptr::null_mut(), A); + + std::ptr::write_unaligned(std::ptr::null_mut(), A); + + std::ptr::write_volatile(std::ptr::null_mut(), A); + + std::ptr::write_bytes::(std::ptr::null_mut(), 42, 0); + } +} diff --git a/tests/ui/invalid_null_ptr_usage.stderr b/tests/ui/invalid_null_ptr_usage.stderr new file mode 100644 index 0000000000000..532c36abe5196 --- /dev/null +++ b/tests/ui/invalid_null_ptr_usage.stderr @@ -0,0 +1,154 @@ +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:5:59 + | +LL | let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null(), 0); + | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + | + = note: `#[deny(clippy::invalid_null_ptr_usage)]` on by default + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:6:59 + | +LL | let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:8:63 + | +LL | let _slice: &[usize] = std::slice::from_raw_parts_mut(std::ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:10:33 + | +LL | std::ptr::copy::(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0); + | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:11:73 + | +LL | std::ptr::copy::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:13:48 + | +LL | std::ptr::copy_nonoverlapping::(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0); + | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:14:88 + | +LL | std::ptr::copy_nonoverlapping::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:19:36 + | +LL | let _a: A = std::ptr::read(std::ptr::null()); + | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:20:36 + | +LL | let _a: A = std::ptr::read(std::ptr::null_mut()); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:22:46 + | +LL | let _a: A = std::ptr::read_unaligned(std::ptr::null()); + | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:23:46 + | +LL | let _a: A = std::ptr::read_unaligned(std::ptr::null_mut()); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:25:45 + | +LL | let _a: A = std::ptr::read_volatile(std::ptr::null()); + | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:26:45 + | +LL | let _a: A = std::ptr::read_volatile(std::ptr::null_mut()); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:28:39 + | +LL | let _a: A = std::ptr::replace(std::ptr::null_mut(), A); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:30:69 + | +LL | let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null(), 0); + | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:31:69 + | +LL | let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:33:73 + | +LL | let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(std::ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:35:29 + | +LL | std::ptr::swap::(std::ptr::null_mut(), &mut A); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:36:37 + | +LL | std::ptr::swap::(&mut A, std::ptr::null_mut()); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:38:44 + | +LL | std::ptr::swap_nonoverlapping::(std::ptr::null_mut(), &mut A, 0); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:39:52 + | +LL | std::ptr::swap_nonoverlapping::(&mut A, std::ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:41:25 + | +LL | std::ptr::write(std::ptr::null_mut(), A); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:43:35 + | +LL | std::ptr::write_unaligned(std::ptr::null_mut(), A); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:45:34 + | +LL | std::ptr::write_volatile(std::ptr::null_mut(), A); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:47:40 + | +LL | std::ptr::write_bytes::(std::ptr::null_mut(), 42, 0); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: aborting due to 25 previous errors + diff --git a/tests/ui/macro_use_imports.fixed b/tests/ui/macro_use_imports.fixed index 91e34c62160a1..51c66a46368db 100644 --- a/tests/ui/macro_use_imports.fixed +++ b/tests/ui/macro_use_imports.fixed @@ -4,7 +4,7 @@ // run-rustfix // ignore-32bit -#![allow(unused_imports, unreachable_code, unused_variables, dead_code)] +#![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)] #![allow(clippy::single_component_path_imports)] #![warn(clippy::macro_use_imports)] @@ -40,4 +40,8 @@ mod a { } } +// issue #7015, ICE due to calling `item_children` with local `DefId` +#[macro_use] +use a as b; + fn main() {} diff --git a/tests/ui/macro_use_imports.rs b/tests/ui/macro_use_imports.rs index 9c3c50c5d49f2..2011129bc944d 100644 --- a/tests/ui/macro_use_imports.rs +++ b/tests/ui/macro_use_imports.rs @@ -4,7 +4,7 @@ // run-rustfix // ignore-32bit -#![allow(unused_imports, unreachable_code, unused_variables, dead_code)] +#![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)] #![allow(clippy::single_component_path_imports)] #![warn(clippy::macro_use_imports)] @@ -40,4 +40,8 @@ mod a { } } +// issue #7015, ICE due to calling `item_children` with local `DefId` +#[macro_use] +use a as b; + fn main() {} diff --git a/tests/ui/manual_map_option.fixed b/tests/ui/manual_map_option.fixed index ee01584577786..40d01df6379a6 100644 --- a/tests/ui/manual_map_option.fixed +++ b/tests/ui/manual_map_option.fixed @@ -146,4 +146,11 @@ fn main() { None => None, }; } + + // #7077 + let s = &String::new(); + let _: Option<&str> = match Some(s) { + Some(s) => Some(s), + None => None, + }; } diff --git a/tests/ui/manual_map_option.rs b/tests/ui/manual_map_option.rs index 29509bddfd94d..cfef0c5cc4ec6 100644 --- a/tests/ui/manual_map_option.rs +++ b/tests/ui/manual_map_option.rs @@ -212,4 +212,11 @@ fn main() { None => None, }; } + + // #7077 + let s = &String::new(); + let _: Option<&str> = match Some(s) { + Some(s) => Some(s), + None => None, + }; } diff --git a/tests/ui/missing_const_for_fn/auxiliary/helper.rs b/tests/ui/missing_const_for_fn/auxiliary/helper.rs new file mode 100644 index 0000000000000..7b9dc76b8f1d4 --- /dev/null +++ b/tests/ui/missing_const_for_fn/auxiliary/helper.rs @@ -0,0 +1,8 @@ +// This file provides a const function that is unstably const forever. + +#![feature(staged_api)] +#![stable(feature = "1", since = "1.0.0")] + +#[stable(feature = "1", since = "1.0.0")] +#[rustc_const_unstable(feature = "foo", issue = "none")] +pub const fn unstably_const_fn() {} diff --git a/tests/ui/missing_const_for_fn/cant_be_const.rs b/tests/ui/missing_const_for_fn/cant_be_const.rs index ba352ef9ee932..7cda1aaa3c228 100644 --- a/tests/ui/missing_const_for_fn/cant_be_const.rs +++ b/tests/ui/missing_const_for_fn/cant_be_const.rs @@ -2,9 +2,14 @@ //! compilation error. //! The .stderr output of this test should be empty. Otherwise it's a bug somewhere. +// aux-build:helper.rs + #![warn(clippy::missing_const_for_fn)] #![allow(incomplete_features)] #![feature(start, const_generics)] +#![feature(custom_inner_attributes)] + +extern crate helper; struct Game; @@ -101,3 +106,17 @@ fn const_generic_return(t: &[T]) -> &[T; N] { unsafe { &*p } } + +// Do not lint this because it calls a function whose constness is unstable. +fn unstably_const_fn() { + helper::unstably_const_fn() +} + +mod const_fn_stabilized_after_msrv { + #![clippy::msrv = "1.46.0"] + + // Do not lint this because `u8::is_ascii_digit` is stabilized as a const function in 1.47.0. + fn const_fn_stabilized_after_msrv(byte: u8) { + byte.is_ascii_digit(); + } +} diff --git a/tests/ui/missing_const_for_fn/could_be_const.rs b/tests/ui/missing_const_for_fn/could_be_const.rs index c6f44b7daa342..0accb516f5f6b 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/tests/ui/missing_const_for_fn/could_be_const.rs @@ -1,6 +1,7 @@ #![warn(clippy::missing_const_for_fn)] #![allow(incomplete_features, clippy::let_and_return)] #![feature(const_generics)] +#![feature(custom_inner_attributes)] use std::mem::transmute; @@ -70,5 +71,14 @@ mod with_drop { } } +mod const_fn_stabilized_before_msrv { + #![clippy::msrv = "1.47.0"] + + // This could be const because `u8::is_ascii_digit` is a stable const function in 1.47. + fn const_fn_stabilized_before_msrv(byte: u8) { + byte.is_ascii_digit(); + } +} + // Should not be const fn main() {} diff --git a/tests/ui/missing_const_for_fn/could_be_const.stderr b/tests/ui/missing_const_for_fn/could_be_const.stderr index 74d32b8a1aa93..63c211f39fa1e 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.stderr +++ b/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -1,5 +1,5 @@ error: this could be a `const fn` - --> $DIR/could_be_const.rs:13:5 + --> $DIR/could_be_const.rs:14:5 | LL | / pub fn new() -> Self { LL | | Self { guess: 42 } @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::missing-const-for-fn` implied by `-D warnings` error: this could be a `const fn` - --> $DIR/could_be_const.rs:17:5 + --> $DIR/could_be_const.rs:18:5 | LL | / fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] { LL | | b @@ -17,7 +17,7 @@ LL | | } | |_____^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:23:1 + --> $DIR/could_be_const.rs:24:1 | LL | / fn one() -> i32 { LL | | 1 @@ -25,7 +25,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:28:1 + --> $DIR/could_be_const.rs:29:1 | LL | / fn two() -> i32 { LL | | let abc = 2; @@ -34,7 +34,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:34:1 + --> $DIR/could_be_const.rs:35:1 | LL | / fn string() -> String { LL | | String::new() @@ -42,7 +42,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:39:1 + --> $DIR/could_be_const.rs:40:1 | LL | / unsafe fn four() -> i32 { LL | | 4 @@ -50,7 +50,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:44:1 + --> $DIR/could_be_const.rs:45:1 | LL | / fn generic(t: T) -> T { LL | | t @@ -58,12 +58,20 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:67:9 + --> $DIR/could_be_const.rs:68:9 | LL | / pub fn b(self, a: &A) -> B { LL | | B LL | | } | |_________^ -error: aborting due to 8 previous errors +error: this could be a `const fn` + --> $DIR/could_be_const.rs:78:5 + | +LL | / fn const_fn_stabilized_before_msrv(byte: u8) { +LL | | byte.is_ascii_digit(); +LL | | } + | |_____^ + +error: aborting due to 9 previous errors diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index 82d95cc041fb3..5c4fd466c0418 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -1,4 +1,5 @@ // run-rustfix +// edition:2018 #![allow(unused)] #![allow( @@ -125,10 +126,85 @@ mod issue6501 { } } -fn main() { - let _ = test_end_of_fn(); - let _ = test_no_semicolon(); - let _ = test_if_block(); - let _ = test_match(true); - test_closure(); +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 main() {} diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 8a471f802e111..34811db7413a3 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -1,4 +1,5 @@ // run-rustfix +// edition:2018 #![allow(unused)] #![allow( @@ -125,10 +126,85 @@ mod issue6501 { } } -fn main() { - let _ = test_end_of_fn(); - let _ = test_no_semicolon(); - let _ = test_if_block(); - let _ = test_match(true); - test_closure(); +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 main() {} diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index 075db22456f73..74dda971fdabb 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -1,5 +1,5 @@ error: unneeded `return` statement - --> $DIR/needless_return.rs:23:5 + --> $DIR/needless_return.rs:24:5 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` @@ -7,106 +7,190 @@ LL | return true; = note: `-D clippy::needless-return` implied by `-D warnings` error: unneeded `return` statement - --> $DIR/needless_return.rs:27:5 + --> $DIR/needless_return.rs:28:5 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:32:9 + --> $DIR/needless_return.rs:33:9 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:34:9 + --> $DIR/needless_return.rs:35:9 | LL | return false; | ^^^^^^^^^^^^^ help: remove `return`: `false` error: unneeded `return` statement - --> $DIR/needless_return.rs:40:17 + --> $DIR/needless_return.rs:41:17 | LL | true => return false, | ^^^^^^^^^^^^ help: remove `return`: `false` error: unneeded `return` statement - --> $DIR/needless_return.rs:42:13 + --> $DIR/needless_return.rs:43:13 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:49:9 + --> $DIR/needless_return.rs:50:9 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:51:16 + --> $DIR/needless_return.rs:52:16 | LL | let _ = || return true; | ^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:59:5 + --> $DIR/needless_return.rs:60:5 | LL | return; | ^^^^^^^ help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:64:9 + --> $DIR/needless_return.rs:65:9 | LL | return; | ^^^^^^^ help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:66:9 + --> $DIR/needless_return.rs:67:9 | LL | return; | ^^^^^^^ help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:73:14 + --> $DIR/needless_return.rs:74:14 | LL | _ => return, | ^^^^^^ help: replace `return` with an empty block: `{}` error: unneeded `return` statement - --> $DIR/needless_return.rs:88: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:90:9 + --> $DIR/needless_return.rs:91:9 | LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()` error: unneeded `return` statement - --> $DIR/needless_return.rs:111:32 + --> $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:116:13 + --> $DIR/needless_return.rs:117:13 | LL | return; | ^^^^^^^ help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:118:20 + --> $DIR/needless_return.rs:119:20 | LL | let _ = || return; | ^^^^^^ help: replace `return` with an empty block: `{}` error: unneeded `return` statement - --> $DIR/needless_return.rs:124:32 + --> $DIR/needless_return.rs:125:32 | LL | res.unwrap_or_else(|_| return Foo) | ^^^^^^^^^^ help: remove `return`: `Foo` -error: aborting due to 18 previous errors +error: unneeded `return` statement + --> $DIR/needless_return.rs:134:5 + | +LL | return true; + | ^^^^^^^^^^^^ help: remove `return`: `true` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:138:5 + | +LL | return true; + | ^^^^^^^^^^^^ help: remove `return`: `true` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:143:9 + | +LL | return true; + | ^^^^^^^^^^^^ help: remove `return`: `true` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:145:9 + | +LL | return false; + | ^^^^^^^^^^^^^ help: remove `return`: `false` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:151:17 + | +LL | true => return false, + | ^^^^^^^^^^^^ help: remove `return`: `false` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:153:13 + | +LL | return true; + | ^^^^^^^^^^^^ help: remove `return`: `true` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:160:9 + | +LL | return true; + | ^^^^^^^^^^^^ help: remove `return`: `true` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:162:16 + | +LL | let _ = || return true; + | ^^^^^^^^^^^ help: remove `return`: `true` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:170:5 + | +LL | return; + | ^^^^^^^ help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:175:9 + | +LL | return; + | ^^^^^^^ help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:177:9 + | +LL | return; + | ^^^^^^^ help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:184:14 + | +LL | _ => return, + | ^^^^^^ help: replace `return` with an empty block: `{}` + +error: unneeded `return` statement + --> $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 diff --git a/tests/ui/panic_in_result_fn_debug_assertions.rs b/tests/ui/panic_in_result_fn_debug_assertions.rs index b60c79f97c865..c4fcd7e70944c 100644 --- a/tests/ui/panic_in_result_fn_debug_assertions.rs +++ b/tests/ui/panic_in_result_fn_debug_assertions.rs @@ -1,44 +1,39 @@ #![warn(clippy::panic_in_result_fn)] #![allow(clippy::unnecessary_wraps)] +// debug_assert should never trigger the `panic_in_result_fn` lint + struct A; impl A { - fn result_with_debug_assert_with_message(x: i32) -> Result // should emit lint - { + fn result_with_debug_assert_with_message(x: i32) -> Result { debug_assert!(x == 5, "wrong argument"); Ok(true) } - fn result_with_debug_assert_eq(x: i32) -> Result // should emit lint - { + fn result_with_debug_assert_eq(x: i32) -> Result { debug_assert_eq!(x, 5); Ok(true) } - fn result_with_debug_assert_ne(x: i32) -> Result // should emit lint - { + fn result_with_debug_assert_ne(x: i32) -> Result { debug_assert_ne!(x, 1); Ok(true) } - fn other_with_debug_assert_with_message(x: i32) // should not emit lint - { + fn other_with_debug_assert_with_message(x: i32) { debug_assert!(x == 5, "wrong argument"); } - fn other_with_debug_assert_eq(x: i32) // should not emit lint - { + fn other_with_debug_assert_eq(x: i32) { debug_assert_eq!(x, 5); } - fn other_with_debug_assert_ne(x: i32) // should not emit lint - { + fn other_with_debug_assert_ne(x: i32) { debug_assert_ne!(x, 1); } - fn result_without_banned_functions() -> Result // should not emit lint - { + fn result_without_banned_functions() -> Result { let debug_assert = "debug_assert!"; println!("No {}", debug_assert); Ok(true) diff --git a/tests/ui/panic_in_result_fn_debug_assertions.stderr b/tests/ui/panic_in_result_fn_debug_assertions.stderr deleted file mode 100644 index ec18e89698c56..0000000000000 --- a/tests/ui/panic_in_result_fn_debug_assertions.stderr +++ /dev/null @@ -1,57 +0,0 @@ -error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` - --> $DIR/panic_in_result_fn_debug_assertions.rs:7:5 - | -LL | / fn result_with_debug_assert_with_message(x: i32) -> Result // should emit lint -LL | | { -LL | | debug_assert!(x == 5, "wrong argument"); -LL | | Ok(true) -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 - --> $DIR/panic_in_result_fn_debug_assertions.rs:9:9 - | -LL | debug_assert!(x == 5, "wrong argument"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in a macro (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_debug_assertions.rs:13:5 - | -LL | / fn result_with_debug_assert_eq(x: i32) -> Result // should emit lint -LL | | { -LL | | debug_assert_eq!(x, 5); -LL | | Ok(true) -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_debug_assertions.rs:15:9 - | -LL | debug_assert_eq!(x, 5); - | ^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in a macro (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_debug_assertions.rs:19:5 - | -LL | / fn result_with_debug_assert_ne(x: i32) -> Result // should emit lint -LL | | { -LL | | debug_assert_ne!(x, 1); -LL | | Ok(true) -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_debug_assertions.rs:21:9 - | -LL | debug_assert_ne!(x, 1); - | ^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 3 previous errors - diff --git a/tests/ui/panicking_macros.rs b/tests/ui/panicking_macros.rs index 77fcb8dfd02fd..93b236f7473d5 100644 --- a/tests/ui/panicking_macros.rs +++ b/tests/ui/panicking_macros.rs @@ -1,5 +1,5 @@ #![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)] -#![allow(clippy::assertions_on_constants)] +#![allow(clippy::assertions_on_constants, clippy::eq_op)] extern crate core; @@ -43,6 +43,18 @@ fn core_versions() { unreachable!(); } +fn debug_assert() { + debug_assert!(true); + debug_assert_eq!(true, true); + debug_assert_ne!(true, false); +} + +fn debug_assert_msg() { + debug_assert!(true, "test"); + debug_assert_eq!(true, true, "test"); + debug_assert_ne!(true, false, "test"); +} + fn main() { panic(); todo(); diff --git a/tests/ui/redundant_pattern_matching_drop_order.fixed b/tests/ui/redundant_pattern_matching_drop_order.fixed new file mode 100644 index 0000000000000..794ed542435d1 --- /dev/null +++ b/tests/ui/redundant_pattern_matching_drop_order.fixed @@ -0,0 +1,58 @@ +// run-rustfix + +// Issue #5746 +#![warn(clippy::redundant_pattern_matching)] +#![allow(clippy::if_same_then_else)] +use std::task::Poll::{Pending, Ready}; + +fn main() { + let m = std::sync::Mutex::new((0, 0)); + + // Result + if m.lock().is_ok() {} + if Err::<(), _>(m.lock().unwrap().0).is_err() {} + + { + if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok() {} + } + if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok() { + } else { + } + if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok() {} + if Err::, _>(()).is_err() {} + + if Ok::<_, ()>(String::new()).is_ok() {} + if Err::<(), _>((String::new(), ())).is_err() {} + + // Option + if Some(m.lock()).is_some() {} + if Some(m.lock().unwrap().0).is_some() {} + + { + if None::>.is_none() {} + } + if None::>.is_none() { + } else { + } + + if None::>.is_none() {} + + if Some(String::new()).is_some() {} + if Some((String::new(), ())).is_some() {} + + // Poll + if Ready(m.lock()).is_ready() {} + if Ready(m.lock().unwrap().0).is_ready() {} + + { + if Pending::>.is_pending() {} + } + if Pending::>.is_pending() { + } else { + } + + if Pending::>.is_pending() {} + + if Ready(String::new()).is_ready() {} + if Ready((String::new(), ())).is_ready() {} +} diff --git a/tests/ui/redundant_pattern_matching_drop_order.rs b/tests/ui/redundant_pattern_matching_drop_order.rs new file mode 100644 index 0000000000000..b9c82d86f618b --- /dev/null +++ b/tests/ui/redundant_pattern_matching_drop_order.rs @@ -0,0 +1,58 @@ +// run-rustfix + +// Issue #5746 +#![warn(clippy::redundant_pattern_matching)] +#![allow(clippy::if_same_then_else)] +use std::task::Poll::{Pending, Ready}; + +fn main() { + let m = std::sync::Mutex::new((0, 0)); + + // Result + if let Ok(_) = m.lock() {} + if let Err(_) = Err::<(), _>(m.lock().unwrap().0) {} + + { + if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {} + } + if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) { + } else { + } + if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {} + if let Err(_) = Err::, _>(()) {} + + if let Ok(_) = Ok::<_, ()>(String::new()) {} + if let Err(_) = Err::<(), _>((String::new(), ())) {} + + // Option + if let Some(_) = Some(m.lock()) {} + if let Some(_) = Some(m.lock().unwrap().0) {} + + { + if let None = None::> {} + } + if let None = None::> { + } else { + } + + if let None = None::> {} + + if let Some(_) = Some(String::new()) {} + if let Some(_) = Some((String::new(), ())) {} + + // Poll + if let Ready(_) = Ready(m.lock()) {} + if let Ready(_) = Ready(m.lock().unwrap().0) {} + + { + if let Pending = Pending::> {} + } + if let Pending = Pending::> { + } else { + } + + if let Pending = Pending::> {} + + if let Ready(_) = Ready(String::new()) {} + if let Ready(_) = Ready((String::new(), ())) {} +} diff --git a/tests/ui/redundant_pattern_matching_drop_order.stderr b/tests/ui/redundant_pattern_matching_drop_order.stderr new file mode 100644 index 0000000000000..eb7aa70ee2738 --- /dev/null +++ b/tests/ui/redundant_pattern_matching_drop_order.stderr @@ -0,0 +1,171 @@ +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_drop_order.rs:12:12 + | +LL | if let Ok(_) = m.lock() {} + | -------^^^^^----------- help: try this: `if m.lock().is_ok()` + | + = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_drop_order.rs:13:12 + | +LL | if let Err(_) = Err::<(), _>(m.lock().unwrap().0) {} + | -------^^^^^^------------------------------------ help: try this: `if Err::<(), _>(m.lock().unwrap().0).is_err()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_drop_order.rs:16:16 + | +LL | if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {} + | -------^^^^^----------------------------------------- help: try this: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_drop_order.rs:18:12 + | +LL | if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) { + | -------^^^^^----------------------------------------- help: try this: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_drop_order.rs:21:12 + | +LL | if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {} + | -------^^^^^----------------------------------------- help: try this: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_drop_order.rs:22:12 + | +LL | if let Err(_) = Err::, _>(()) {} + | -------^^^^^^------------------------------------------ help: try this: `if Err::, _>(()).is_err()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_drop_order.rs:24:12 + | +LL | if let Ok(_) = Ok::<_, ()>(String::new()) {} + | -------^^^^^----------------------------- help: try this: `if Ok::<_, ()>(String::new()).is_ok()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_drop_order.rs:25:12 + | +LL | if let Err(_) = Err::<(), _>((String::new(), ())) {} + | -------^^^^^^------------------------------------ help: try this: `if Err::<(), _>((String::new(), ())).is_err()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_drop_order.rs:28:12 + | +LL | if let Some(_) = Some(m.lock()) {} + | -------^^^^^^^----------------- help: try this: `if Some(m.lock()).is_some()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_drop_order.rs:29:12 + | +LL | if let Some(_) = Some(m.lock().unwrap().0) {} + | -------^^^^^^^---------------------------- help: try this: `if Some(m.lock().unwrap().0).is_some()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching_drop_order.rs:32:16 + | +LL | if let None = None::> {} + | -------^^^^------------------------------------ help: try this: `if None::>.is_none()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching_drop_order.rs:34:12 + | +LL | if let None = None::> { + | -------^^^^------------------------------------ help: try this: `if None::>.is_none()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching_drop_order.rs:38:12 + | +LL | if let None = None::> {} + | -------^^^^------------------------------------ help: try this: `if None::>.is_none()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_drop_order.rs:40:12 + | +LL | if let Some(_) = Some(String::new()) {} + | -------^^^^^^^---------------------- help: try this: `if Some(String::new()).is_some()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_drop_order.rs:41:12 + | +LL | if let Some(_) = Some((String::new(), ())) {} + | -------^^^^^^^---------------------------- help: try this: `if Some((String::new(), ())).is_some()` + +error: redundant pattern matching, consider using `is_ready()` + --> $DIR/redundant_pattern_matching_drop_order.rs:44:12 + | +LL | if let Ready(_) = Ready(m.lock()) {} + | -------^^^^^^^^------------------ help: try this: `if Ready(m.lock()).is_ready()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_ready()` + --> $DIR/redundant_pattern_matching_drop_order.rs:45:12 + | +LL | if let Ready(_) = Ready(m.lock().unwrap().0) {} + | -------^^^^^^^^----------------------------- help: try this: `if Ready(m.lock().unwrap().0).is_ready()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_pending()` + --> $DIR/redundant_pattern_matching_drop_order.rs:48:16 + | +LL | if let Pending = Pending::> {} + | -------^^^^^^^--------------------------------------- help: try this: `if Pending::>.is_pending()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_pending()` + --> $DIR/redundant_pattern_matching_drop_order.rs:50:12 + | +LL | if let Pending = Pending::> { + | -------^^^^^^^--------------------------------------- help: try this: `if Pending::>.is_pending()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_pending()` + --> $DIR/redundant_pattern_matching_drop_order.rs:54:12 + | +LL | if let Pending = Pending::> {} + | -------^^^^^^^--------------------------------------- help: try this: `if Pending::>.is_pending()` + +error: redundant pattern matching, consider using `is_ready()` + --> $DIR/redundant_pattern_matching_drop_order.rs:56:12 + | +LL | if let Ready(_) = Ready(String::new()) {} + | -------^^^^^^^^----------------------- help: try this: `if Ready(String::new()).is_ready()` + +error: redundant pattern matching, consider using `is_ready()` + --> $DIR/redundant_pattern_matching_drop_order.rs:57:12 + | +LL | if let Ready(_) = Ready((String::new(), ())) {} + | -------^^^^^^^^----------------------------- help: try this: `if Ready((String::new(), ())).is_ready()` + +error: aborting due to 22 previous errors + diff --git a/tests/ui/redundant_pattern_matching_option.fixed b/tests/ui/redundant_pattern_matching_option.fixed index 66f580a0a6834..997144772669b 100644 --- a/tests/ui/redundant_pattern_matching_option.fixed +++ b/tests/ui/redundant_pattern_matching_option.fixed @@ -2,7 +2,12 @@ #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] -#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] +#![allow( + unused_must_use, + clippy::needless_bool, + clippy::match_like_matches_macro, + clippy::if_same_then_else +)] fn main() { if None::<()>.is_none() {} diff --git a/tests/ui/redundant_pattern_matching_option.rs b/tests/ui/redundant_pattern_matching_option.rs index f18b27b8b95c3..8309847e18162 100644 --- a/tests/ui/redundant_pattern_matching_option.rs +++ b/tests/ui/redundant_pattern_matching_option.rs @@ -2,7 +2,12 @@ #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] -#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] +#![allow( + unused_must_use, + clippy::needless_bool, + clippy::match_like_matches_macro, + clippy::if_same_then_else +)] fn main() { if let None = None::<()> {} diff --git a/tests/ui/redundant_pattern_matching_option.stderr b/tests/ui/redundant_pattern_matching_option.stderr index 58482a0ab70dc..613a30d4a4845 100644 --- a/tests/ui/redundant_pattern_matching_option.stderr +++ b/tests/ui/redundant_pattern_matching_option.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:8:12 + --> $DIR/redundant_pattern_matching_option.rs:13:12 | LL | if let None = None::<()> {} | -------^^^^------------- help: try this: `if None::<()>.is_none()` @@ -7,43 +7,43 @@ LL | if let None = None::<()> {} = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:10:12 + --> $DIR/redundant_pattern_matching_option.rs:15:12 | LL | if let Some(_) = Some(42) {} | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:12:12 + --> $DIR/redundant_pattern_matching_option.rs:17:12 | LL | if let Some(_) = Some(42) { | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:18:15 + --> $DIR/redundant_pattern_matching_option.rs:23:15 | LL | while let Some(_) = Some(42) {} | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:20:15 + --> $DIR/redundant_pattern_matching_option.rs:25:15 | LL | while let None = Some(42) {} | ----------^^^^----------- help: try this: `while Some(42).is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:22:15 + --> $DIR/redundant_pattern_matching_option.rs:27:15 | LL | while let None = None::<()> {} | ----------^^^^------------- help: try this: `while None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:25:15 + --> $DIR/redundant_pattern_matching_option.rs:30:15 | LL | while let Some(_) = v.pop() { | ----------^^^^^^^---------- help: try this: `while v.pop().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:33:5 + --> $DIR/redundant_pattern_matching_option.rs:38:5 | LL | / match Some(42) { LL | | Some(_) => true, @@ -52,7 +52,7 @@ LL | | }; | |_____^ help: try this: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:38:5 + --> $DIR/redundant_pattern_matching_option.rs:43:5 | LL | / match None::<()> { LL | | Some(_) => false, @@ -61,7 +61,7 @@ LL | | }; | |_____^ help: try this: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:43:13 + --> $DIR/redundant_pattern_matching_option.rs:48:13 | LL | let _ = match None::<()> { | _____________^ @@ -71,49 +71,49 @@ LL | | }; | |_____^ help: try this: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:49:20 + --> $DIR/redundant_pattern_matching_option.rs:54:20 | LL | let _ = if let Some(_) = opt { true } else { false }; | -------^^^^^^^------ help: try this: `if opt.is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:53:20 + --> $DIR/redundant_pattern_matching_option.rs:58:20 | LL | let _ = if let Some(_) = gen_opt() { | -------^^^^^^^------------ help: try this: `if gen_opt().is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:55:19 + --> $DIR/redundant_pattern_matching_option.rs:60:19 | LL | } else if let None = gen_opt() { | -------^^^^------------ help: try this: `if gen_opt().is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:74:12 + --> $DIR/redundant_pattern_matching_option.rs:79:12 | LL | if let Some(_) = Some(42) {} | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:76:12 + --> $DIR/redundant_pattern_matching_option.rs:81:12 | LL | if let None = None::<()> {} | -------^^^^------------- help: try this: `if None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:78:15 + --> $DIR/redundant_pattern_matching_option.rs:83:15 | LL | while let Some(_) = Some(42) {} | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:80:15 + --> $DIR/redundant_pattern_matching_option.rs:85:15 | LL | while let None = None::<()> {} | ----------^^^^------------- help: try this: `while None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:82:5 + --> $DIR/redundant_pattern_matching_option.rs:87:5 | LL | / match Some(42) { LL | | Some(_) => true, @@ -122,7 +122,7 @@ LL | | }; | |_____^ help: try this: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:87:5 + --> $DIR/redundant_pattern_matching_option.rs:92:5 | LL | / match None::<()> { LL | | Some(_) => false, diff --git a/tests/ui/redundant_pattern_matching_poll.fixed b/tests/ui/redundant_pattern_matching_poll.fixed index 465aa80dac27c..c297745380404 100644 --- a/tests/ui/redundant_pattern_matching_poll.fixed +++ b/tests/ui/redundant_pattern_matching_poll.fixed @@ -2,7 +2,12 @@ #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] -#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] +#![allow( + unused_must_use, + clippy::needless_bool, + clippy::match_like_matches_macro, + clippy::if_same_then_else +)] use std::task::Poll::{self, Pending, Ready}; diff --git a/tests/ui/redundant_pattern_matching_poll.rs b/tests/ui/redundant_pattern_matching_poll.rs index 7891ff353b13e..665c8c417504d 100644 --- a/tests/ui/redundant_pattern_matching_poll.rs +++ b/tests/ui/redundant_pattern_matching_poll.rs @@ -2,7 +2,12 @@ #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] -#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] +#![allow( + unused_must_use, + clippy::needless_bool, + clippy::match_like_matches_macro, + clippy::if_same_then_else +)] use std::task::Poll::{self, Pending, Ready}; diff --git a/tests/ui/redundant_pattern_matching_poll.stderr b/tests/ui/redundant_pattern_matching_poll.stderr index 5ffc6c47c90a2..5ecf024a733a3 100644 --- a/tests/ui/redundant_pattern_matching_poll.stderr +++ b/tests/ui/redundant_pattern_matching_poll.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:10:12 + --> $DIR/redundant_pattern_matching_poll.rs:15:12 | LL | if let Pending = Pending::<()> {} | -------^^^^^^^---------------- help: try this: `if Pending::<()>.is_pending()` @@ -7,37 +7,37 @@ LL | if let Pending = Pending::<()> {} = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:12:12 + --> $DIR/redundant_pattern_matching_poll.rs:17:12 | LL | if let Ready(_) = Ready(42) {} | -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:14:12 + --> $DIR/redundant_pattern_matching_poll.rs:19:12 | LL | if let Ready(_) = Ready(42) { | -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:20:15 + --> $DIR/redundant_pattern_matching_poll.rs:25:15 | LL | while let Ready(_) = Ready(42) {} | ----------^^^^^^^^------------ help: try this: `while Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:22:15 + --> $DIR/redundant_pattern_matching_poll.rs:27:15 | LL | while let Pending = Ready(42) {} | ----------^^^^^^^------------ help: try this: `while Ready(42).is_pending()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:24:15 + --> $DIR/redundant_pattern_matching_poll.rs:29:15 | LL | while let Pending = Pending::<()> {} | ----------^^^^^^^---------------- help: try this: `while Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:30:5 + --> $DIR/redundant_pattern_matching_poll.rs:35:5 | LL | / match Ready(42) { LL | | Ready(_) => true, @@ -46,7 +46,7 @@ LL | | }; | |_____^ help: try this: `Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:35:5 + --> $DIR/redundant_pattern_matching_poll.rs:40:5 | LL | / match Pending::<()> { LL | | Ready(_) => false, @@ -55,7 +55,7 @@ LL | | }; | |_____^ help: try this: `Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:40:13 + --> $DIR/redundant_pattern_matching_poll.rs:45:13 | LL | let _ = match Pending::<()> { | _____________^ @@ -65,49 +65,49 @@ LL | | }; | |_____^ help: try this: `Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:46:20 + --> $DIR/redundant_pattern_matching_poll.rs:51:20 | LL | let _ = if let Ready(_) = poll { true } else { false }; | -------^^^^^^^^------- help: try this: `if poll.is_ready()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:50:20 + --> $DIR/redundant_pattern_matching_poll.rs:55:20 | LL | let _ = if let Ready(_) = gen_poll() { | -------^^^^^^^^------------- help: try this: `if gen_poll().is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:52:19 + --> $DIR/redundant_pattern_matching_poll.rs:57:19 | LL | } else if let Pending = gen_poll() { | -------^^^^^^^------------- help: try this: `if gen_poll().is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:68:12 + --> $DIR/redundant_pattern_matching_poll.rs:73:12 | LL | if let Ready(_) = Ready(42) {} | -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:70:12 + --> $DIR/redundant_pattern_matching_poll.rs:75:12 | LL | if let Pending = Pending::<()> {} | -------^^^^^^^---------------- help: try this: `if Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:72:15 + --> $DIR/redundant_pattern_matching_poll.rs:77:15 | LL | while let Ready(_) = Ready(42) {} | ----------^^^^^^^^------------ help: try this: `while Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:74:15 + --> $DIR/redundant_pattern_matching_poll.rs:79:15 | LL | while let Pending = Pending::<()> {} | ----------^^^^^^^---------------- help: try this: `while Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:76:5 + --> $DIR/redundant_pattern_matching_poll.rs:81:5 | LL | / match Ready(42) { LL | | Ready(_) => true, @@ -116,7 +116,7 @@ LL | | }; | |_____^ help: try this: `Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:81:5 + --> $DIR/redundant_pattern_matching_poll.rs:86:5 | LL | / match Pending::<()> { LL | | Ready(_) => false, diff --git a/tests/ui/redundant_pattern_matching_result.fixed b/tests/ui/redundant_pattern_matching_result.fixed index e94c5704b4891..d7af5d762ae4a 100644 --- a/tests/ui/redundant_pattern_matching_result.fixed +++ b/tests/ui/redundant_pattern_matching_result.fixed @@ -7,7 +7,8 @@ clippy::needless_bool, clippy::match_like_matches_macro, clippy::unnecessary_wraps, - deprecated + deprecated, + clippy::if_same_then_else )] fn main() { diff --git a/tests/ui/redundant_pattern_matching_result.rs b/tests/ui/redundant_pattern_matching_result.rs index 5d1752942322b..e06d4485ae4f2 100644 --- a/tests/ui/redundant_pattern_matching_result.rs +++ b/tests/ui/redundant_pattern_matching_result.rs @@ -7,7 +7,8 @@ clippy::needless_bool, clippy::match_like_matches_macro, clippy::unnecessary_wraps, - deprecated + deprecated, + clippy::if_same_then_else )] fn main() { diff --git a/tests/ui/redundant_pattern_matching_result.stderr b/tests/ui/redundant_pattern_matching_result.stderr index d6a46babb7795..e06f095da20c6 100644 --- a/tests/ui/redundant_pattern_matching_result.stderr +++ b/tests/ui/redundant_pattern_matching_result.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:15:12 + --> $DIR/redundant_pattern_matching_result.rs:16:12 | LL | if let Ok(_) = &result {} | -------^^^^^---------- help: try this: `if result.is_ok()` @@ -7,31 +7,31 @@ LL | if let Ok(_) = &result {} = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:17:12 + --> $DIR/redundant_pattern_matching_result.rs:18:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:19:12 + --> $DIR/redundant_pattern_matching_result.rs:20:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:21:15 + --> $DIR/redundant_pattern_matching_result.rs:22:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:23:15 + --> $DIR/redundant_pattern_matching_result.rs:24:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:33:5 + --> $DIR/redundant_pattern_matching_result.rs:34:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -40,7 +40,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:38:5 + --> $DIR/redundant_pattern_matching_result.rs:39:5 | LL | / match Ok::(42) { LL | | Ok(_) => false, @@ -49,7 +49,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_err()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:43:5 + --> $DIR/redundant_pattern_matching_result.rs:44:5 | LL | / match Err::(42) { LL | | Ok(_) => false, @@ -58,7 +58,7 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:48:5 + --> $DIR/redundant_pattern_matching_result.rs:49:5 | LL | / match Err::(42) { LL | | Ok(_) => true, @@ -67,73 +67,73 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_ok()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:53:20 + --> $DIR/redundant_pattern_matching_result.rs:54:20 | LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; | -------^^^^^--------------------- help: try this: `if Ok::(4).is_ok()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:59:20 + --> $DIR/redundant_pattern_matching_result.rs:60:20 | LL | let _ = if let Ok(_) = gen_res() { | -------^^^^^------------ help: try this: `if gen_res().is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:61:19 + --> $DIR/redundant_pattern_matching_result.rs:62:19 | LL | } else if let Err(_) = gen_res() { | -------^^^^^^------------ help: try this: `if gen_res().is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:84:19 + --> $DIR/redundant_pattern_matching_result.rs:85:19 | LL | while let Some(_) = r#try!(result_opt()) {} | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:85:16 + --> $DIR/redundant_pattern_matching_result.rs:86:16 | LL | if let Some(_) = r#try!(result_opt()) {} | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:91:12 + --> $DIR/redundant_pattern_matching_result.rs:92:12 | LL | if let Some(_) = m!() {} | -------^^^^^^^------- help: try this: `if m!().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:92:15 + --> $DIR/redundant_pattern_matching_result.rs:93:15 | LL | while let Some(_) = m!() {} | ----------^^^^^^^------- help: try this: `while m!().is_some()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:110:12 + --> $DIR/redundant_pattern_matching_result.rs:111:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:112:12 + --> $DIR/redundant_pattern_matching_result.rs:113:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:114:15 + --> $DIR/redundant_pattern_matching_result.rs:115:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:116:15 + --> $DIR/redundant_pattern_matching_result.rs:117:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:118:5 + --> $DIR/redundant_pattern_matching_result.rs:119:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -142,7 +142,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:123:5 + --> $DIR/redundant_pattern_matching_result.rs:124:5 | LL | / match Err::(42) { LL | | Ok(_) => false, diff --git a/tests/ui/repl_uninit.rs b/tests/ui/repl_uninit.rs index ad5b8e4857d17..6c7e2b854dc14 100644 --- a/tests/ui/repl_uninit.rs +++ b/tests/ui/repl_uninit.rs @@ -1,5 +1,5 @@ -#![allow(deprecated, invalid_value)] -#![warn(clippy::all)] +#![allow(deprecated, invalid_value, clippy::uninit_assumed_init)] +#![warn(clippy::mem_replace_with_uninit)] use std::mem; diff --git a/tests/ui/single_component_path_imports.fixed b/tests/ui/single_component_path_imports.fixed index a7a8499b58f00..f66b445b7b6a3 100644 --- a/tests/ui/single_component_path_imports.fixed +++ b/tests/ui/single_component_path_imports.fixed @@ -19,3 +19,16 @@ fn main() { // 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() {} +} diff --git a/tests/ui/single_component_path_imports.rs b/tests/ui/single_component_path_imports.rs index 9a427e90ad3df..09d4865859584 100644 --- a/tests/ui/single_component_path_imports.rs +++ b/tests/ui/single_component_path_imports.rs @@ -19,3 +19,16 @@ fn main() { // 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() {} +} diff --git a/tests/ui/single_component_path_imports.stderr b/tests/ui/single_component_path_imports.stderr index 519ada0169a6d..7005fa8f125d3 100644 --- a/tests/ui/single_component_path_imports.stderr +++ b/tests/ui/single_component_path_imports.stderr @@ -1,10 +1,16 @@ +error: this import is redundant + --> $DIR/single_component_path_imports.rs:24: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:6:1 | LL | use regex; | ^^^^^^^^^^ help: remove it entirely - | - = note: `-D clippy::single-component-path-imports` implied by `-D warnings` -error: aborting due to previous error +error: aborting due to 2 previous errors diff --git a/tests/ui/single_component_path_imports_nested_first.rs b/tests/ui/single_component_path_imports_nested_first.rs new file mode 100644 index 0000000000000..94117061b270d --- /dev/null +++ b/tests/ui/single_component_path_imports_nested_first.rs @@ -0,0 +1,17 @@ +// 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() {} +} diff --git a/tests/ui/single_component_path_imports_nested_first.stderr b/tests/ui/single_component_path_imports_nested_first.stderr new file mode 100644 index 0000000000000..0c3256c1ce43a --- /dev/null +++ b/tests/ui/single_component_path_imports_nested_first.stderr @@ -0,0 +1,25 @@ +error: this import is redundant + --> $DIR/single_component_path_imports_nested_first.rs:14: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:14:17 + | +LL | use {regex, serde}; + | ^^^^^ + | + = help: remove this import + +error: this import is redundant + --> $DIR/single_component_path_imports_nested_first.rs:5:1 + | +LL | use regex; + | ^^^^^^^^^^ help: remove it entirely + +error: aborting due to 3 previous errors + diff --git a/tests/ui/single_component_path_imports_self_after.rs b/tests/ui/single_component_path_imports_self_after.rs new file mode 100644 index 0000000000000..94319ade0ac4b --- /dev/null +++ b/tests/ui/single_component_path_imports_self_after.rs @@ -0,0 +1,16 @@ +// 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() {} diff --git a/tests/ui/single_component_path_imports_self_before.rs b/tests/ui/single_component_path_imports_self_before.rs new file mode 100644 index 0000000000000..c7437b234566a --- /dev/null +++ b/tests/ui/single_component_path_imports_self_before.rs @@ -0,0 +1,17 @@ +// 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() {} diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index ca884b41c4579..b1819e08d53bf 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -135,6 +135,14 @@ fn if_suggestion() { Bar::A => println!(), _ => (), } + + // issue #7038 + struct X; + let x = Some(X); + match x { + None => println!(), + _ => (), + }; } macro_rules! single_match { diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index 7ea6955b7401e..9ef2a8668a6fa 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -119,5 +119,14 @@ LL | | _ => (), LL | | } | |_____^ help: try this: `if let Bar::A = x { println!() }` -error: aborting due to 12 previous errors +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:142:5 + | +LL | / match x { +LL | | None => println!(), +LL | | _ => (), +LL | | }; + | |_____^ help: try this: `if let None = x { println!() }` + +error: aborting due to 13 previous errors diff --git a/tests/ui/string_lit_as_bytes.fixed b/tests/ui/string_lit_as_bytes.fixed index dd22bfa5c53ef..df2256e4f97de 100644 --- a/tests/ui/string_lit_as_bytes.fixed +++ b/tests/ui/string_lit_as_bytes.fixed @@ -22,7 +22,7 @@ fn str_lit_as_bytes() { let current_version = env!("CARGO_PKG_VERSION").as_bytes(); - let includestr = include_bytes!("entry_unfixable.rs"); + let includestr = include_bytes!("string_lit_as_bytes.rs"); let _ = b"string with newline\t\n"; } diff --git a/tests/ui/string_lit_as_bytes.rs b/tests/ui/string_lit_as_bytes.rs index d2a710ed6b8ca..c6bf8f732ed9f 100644 --- a/tests/ui/string_lit_as_bytes.rs +++ b/tests/ui/string_lit_as_bytes.rs @@ -22,7 +22,7 @@ fn str_lit_as_bytes() { let current_version = env!("CARGO_PKG_VERSION").as_bytes(); - let includestr = include_str!("entry_unfixable.rs").as_bytes(); + let includestr = include_str!("string_lit_as_bytes.rs").as_bytes(); let _ = "string with newline\t\n".as_bytes(); } diff --git a/tests/ui/string_lit_as_bytes.stderr b/tests/ui/string_lit_as_bytes.stderr index e0ddb070b5044..f47d6161c6cf2 100644 --- a/tests/ui/string_lit_as_bytes.stderr +++ b/tests/ui/string_lit_as_bytes.stderr @@ -27,8 +27,8 @@ LL | let bs = "lit to owned".to_owned().into_bytes(); error: calling `as_bytes()` on `include_str!(..)` --> $DIR/string_lit_as_bytes.rs:25:22 | -LL | let includestr = include_str!("entry_unfixable.rs").as_bytes(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `include_bytes!(..)` instead: `include_bytes!("entry_unfixable.rs")` +LL | let includestr = include_str!("string_lit_as_bytes.rs").as_bytes(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `include_bytes!(..)` instead: `include_bytes!("string_lit_as_bytes.rs")` error: calling `as_bytes()` on a string literal --> $DIR/string_lit_as_bytes.rs:27:13 diff --git a/tests/ui/suspicious_else_formatting.rs b/tests/ui/suspicious_else_formatting.rs index 226010ec6df3a..547615b10d9fb 100644 --- a/tests/ui/suspicious_else_formatting.rs +++ b/tests/ui/suspicious_else_formatting.rs @@ -40,6 +40,7 @@ fn main() { { } + // This is fine, though weird. Allman style braces on the else. if foo() { } else @@ -76,4 +77,29 @@ fn main() { } if foo() { } + + // Almost Allman style braces. Lint these. + if foo() { + } + + else + { + + } + + if foo() { + } + else + + { + + } + + // #3864 - Allman style braces + if foo() + { + } + else + { + } } diff --git a/tests/ui/suspicious_else_formatting.stderr b/tests/ui/suspicious_else_formatting.stderr index bbc036d376fef..d8d67b4138ab3 100644 --- a/tests/ui/suspicious_else_formatting.stderr +++ b/tests/ui/suspicious_else_formatting.stderr @@ -41,37 +41,50 @@ LL | | { | = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}` -error: this is an `else {..}` but the formatting might hide it - --> $DIR/suspicious_else_formatting.rs:44:6 +error: this is an `else if` but the formatting might hide it + --> $DIR/suspicious_else_formatting.rs:51:6 | -LL | } +LL | } else | ______^ -LL | | else -LL | | { +LL | | if foo() { // the span of the above error should continue here | |____^ | - = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}` + = note: to remove this lint, remove the `else` or remove the new line between `else` and `if` error: this is an `else if` but the formatting might hide it - --> $DIR/suspicious_else_formatting.rs:50:6 + --> $DIR/suspicious_else_formatting.rs:56:6 | -LL | } else +LL | } | ______^ +LL | | else LL | | if foo() { // the span of the above error should continue here | |____^ | = note: to remove this lint, remove the `else` or remove the new line between `else` and `if` -error: this is an `else if` but the formatting might hide it - --> $DIR/suspicious_else_formatting.rs:55:6 +error: this is an `else {..}` but the formatting might hide it + --> $DIR/suspicious_else_formatting.rs:83:6 | LL | } | ______^ +LL | | LL | | else -LL | | if foo() { // the span of the above error should continue here +LL | | { | |____^ | - = note: to remove this lint, remove the `else` or remove the new line between `else` and `if` + = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}` + +error: this is an `else {..}` but the formatting might hide it + --> $DIR/suspicious_else_formatting.rs:91:6 + | +LL | } + | ______^ +LL | | else +LL | | +LL | | { + | |____^ + | + = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}` -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/try_err.fixed b/tests/ui/try_err.fixed index 5b96bb59c5f18..264194419c739 100644 --- a/tests/ui/try_err.fixed +++ b/tests/ui/try_err.fixed @@ -160,3 +160,11 @@ pub fn poll_next(ready: bool) -> Poll>> { Poll::Ready(None) } + +// Tests that `return` is not duplicated +pub fn try_return(x: bool) -> Result { + if x { + return Err(42); + } + Ok(0) +} diff --git a/tests/ui/try_err.rs b/tests/ui/try_err.rs index f220d697d2cd7..bc6979bf45719 100644 --- a/tests/ui/try_err.rs +++ b/tests/ui/try_err.rs @@ -160,3 +160,11 @@ pub fn poll_next(ready: bool) -> Poll>> { Poll::Ready(None) } + +// Tests that `return` is not duplicated +pub fn try_return(x: bool) -> Result { + if x { + return Err(42)?; + } + Ok(0) +} diff --git a/tests/ui/try_err.stderr b/tests/ui/try_err.stderr index 2c01d37192e8e..8f332a9b6492c 100644 --- a/tests/ui/try_err.stderr +++ b/tests/ui/try_err.stderr @@ -74,5 +74,11 @@ error: returning an `Err(_)` with the `?` operator LL | Err(io::ErrorKind::NotFound)? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))` -error: aborting due to 10 previous errors +error: returning an `Err(_)` with the `?` operator + --> $DIR/try_err.rs:167:16 + | +LL | return Err(42)?; + | ^^^^^^^^ help: try this: `Err(42)` + +error: aborting due to 11 previous errors diff --git a/tests/ui/uninit.rs b/tests/ui/uninit.rs index f42b884e0f0e5..1ed3883c1f060 100644 --- a/tests/ui/uninit.rs +++ b/tests/ui/uninit.rs @@ -1,6 +1,6 @@ #![feature(stmt_expr_attributes)] -use std::mem::MaybeUninit; +use std::mem::{self, MaybeUninit}; fn main() { let _: usize = unsafe { MaybeUninit::uninit().assume_init() }; @@ -19,4 +19,7 @@ fn main() { // This is OK, because all constitutent types are uninit-compatible. let _: (MaybeUninit, [MaybeUninit; 2]) = unsafe { MaybeUninit::uninit().assume_init() }; + + // Was a false negative. + let _: usize = unsafe { mem::MaybeUninit::uninit().assume_init() }; } diff --git a/tests/ui/uninit.stderr b/tests/ui/uninit.stderr index a37233ecddaee..85b64a8419ab0 100644 --- a/tests/ui/uninit.stderr +++ b/tests/ui/uninit.stderr @@ -12,5 +12,11 @@ error: this call for this type may be undefined behavior LL | let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: this call for this type may be undefined behavior + --> $DIR/uninit.rs:24:29 + | +LL | let _: usize = unsafe { mem::MaybeUninit::uninit().assume_init() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors diff --git a/tests/ui/unnecessary_self_imports.fixed b/tests/ui/unnecessary_self_imports.fixed new file mode 100644 index 0000000000000..1185eaa1d552f --- /dev/null +++ b/tests/ui/unnecessary_self_imports.fixed @@ -0,0 +1,10 @@ +// run-rustfix +#![warn(clippy::unnecessary_self_imports)] +#![allow(unused_imports, dead_code)] + +use std::collections::hash_map::{self, *}; +use std::fs as alias; +use std::io::{self, Read}; +use std::rc; + +fn main() {} diff --git a/tests/ui/unnecessary_self_imports.rs b/tests/ui/unnecessary_self_imports.rs new file mode 100644 index 0000000000000..56bfbc09402ad --- /dev/null +++ b/tests/ui/unnecessary_self_imports.rs @@ -0,0 +1,10 @@ +// run-rustfix +#![warn(clippy::unnecessary_self_imports)] +#![allow(unused_imports, dead_code)] + +use std::collections::hash_map::{self, *}; +use std::fs::{self as alias}; +use std::io::{self, Read}; +use std::rc::{self}; + +fn main() {} diff --git a/tests/ui/unnecessary_self_imports.stderr b/tests/ui/unnecessary_self_imports.stderr new file mode 100644 index 0000000000000..83a5618c983d3 --- /dev/null +++ b/tests/ui/unnecessary_self_imports.stderr @@ -0,0 +1,23 @@ +error: import ending with `::{self}` + --> $DIR/unnecessary_self_imports.rs:6:1 + | +LL | use std::fs::{self as alias}; + | ^^^^^^^^^-------------------- + | | + | help: consider omitting `::{self}`: `fs as alias;` + | + = note: `-D clippy::unnecessary-self-imports` implied by `-D warnings` + = note: this will slightly change semantics; any non-module items at the same path will also be imported + +error: import ending with `::{self}` + --> $DIR/unnecessary_self_imports.rs:8:1 + | +LL | use std::rc::{self}; + | ^^^^^^^^^----------- + | | + | help: consider omitting `::{self}`: `rc;` + | + = note: this will slightly change semantics; any non-module items at the same path will also be imported + +error: aborting due to 2 previous errors + diff --git a/tests/ui/wrong_self_convention2.rs b/tests/ui/wrong_self_convention2.rs index 8b42aa59e1323..ae3a740d405d6 100644 --- a/tests/ui/wrong_self_convention2.rs +++ b/tests/ui/wrong_self_convention2.rs @@ -30,3 +30,15 @@ mod issue6983 { } } } + +mod issue7032 { + trait Foo { + fn from_usize(x: usize) -> Self; + } + // don't trigger + impl Foo for usize { + fn from_usize(x: usize) -> Self { + x + } + } +} diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index 082cb35c2e034..27ecb532dd00e 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -133,7 +133,7 @@ opacity: 30%; } - p > code { + :not(pre) > code { color: var(--inline-code-color); background-color: var(--inline-code-bg); } From 22f8c13cf5650d6c9d6ee7b4f0e88bffba9076ca Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 1 Apr 2021 22:46:10 -0400 Subject: [PATCH 03/67] Improve `implicit_return` Better suggestions when returning macro calls. Suggest changeing all the break expressions in a loop, not just the final statement. Don't lint divergent functions. Don't suggest returning the result of any divergent fuction. --- clippy_lints/src/implicit_return.rs | 241 ++++++++++++++++++---------- clippy_utils/src/source.rs | 31 ++-- clippy_utils/src/visitors.rs | 55 ++++++- tests/ui/implicit_return.fixed | 62 +++++-- tests/ui/implicit_return.rs | 62 +++++-- tests/ui/implicit_return.stderr | 55 +++++-- 6 files changed, 366 insertions(+), 140 deletions(-) diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index 6b379b0d59b2b..251a73618716f 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -1,13 +1,15 @@ -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::match_panic_def_id; -use clippy_utils::source::snippet_opt; -use if_chain::if_chain; +use clippy_utils::{ + diagnostics::span_lint_and_sugg, + source::{snippet_with_applicability, snippet_with_context, walk_span_to_context}, + visitors::visit_break_exprs, +}; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, MatchSource, StmtKind}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, HirId}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; +use rustc_span::{Span, SyntaxContext}; declare_clippy_lint! { /// **What it does:** Checks for missing return statements at the end of a block. @@ -39,89 +41,159 @@ declare_clippy_lint! { declare_lint_pass!(ImplicitReturn => [IMPLICIT_RETURN]); -static LINT_BREAK: &str = "change `break` to `return` as shown"; -static LINT_RETURN: &str = "add `return` as shown"; - -fn lint(cx: &LateContext<'_>, outer_span: Span, inner_span: Span, msg: &str) { - let outer_span = outer_span.source_callsite(); - let inner_span = inner_span.source_callsite(); - - span_lint_and_then(cx, IMPLICIT_RETURN, outer_span, "missing `return` statement", |diag| { - if let Some(snippet) = snippet_opt(cx, inner_span) { - diag.span_suggestion( - outer_span, - msg, - format!("return {}", snippet), - Applicability::MachineApplicable, - ); - } - }); +fn lint_return(cx: &LateContext<'_>, span: Span) { + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_applicability(cx, span, "..", &mut app); + span_lint_and_sugg( + cx, + IMPLICIT_RETURN, + span, + "missing `return` statement", + "add `return` as shown", + format!("return {}", snip), + app, + ); +} + +fn lint_break(cx: &LateContext<'_>, break_span: Span, expr_span: Span) { + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_context(cx, expr_span, break_span.ctxt(), "..", &mut app).0; + span_lint_and_sugg( + cx, + IMPLICIT_RETURN, + break_span, + "missing `return` statement", + "change `break` to `return` as shown", + format!("return {}", snip), + app, + ) +} + +enum LintLocation { + /// The lint was applied to a parent expression. + Parent, + /// The lint was applied to this expression, a child, or not applied. + Inner, } +impl LintLocation { + fn still_parent(self, b: bool) -> Self { + if b { self } else { Self::Inner } + } -fn expr_match(cx: &LateContext<'_>, expr: &Expr<'_>) { + fn is_parent(&self) -> bool { + matches!(*self, Self::Parent) + } +} + +// Gets the call site if the span is in a child context. Otherwise returns `None`. +fn get_call_site(span: Span, ctxt: SyntaxContext) -> Option { + (span.ctxt() != ctxt).then(|| walk_span_to_context(span, ctxt).unwrap_or(span)) +} + +fn lint_implicit_returns( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + // The context of the function body. + ctxt: SyntaxContext, + // Whether the expression is from a macro expansion. + call_site_span: Option, +) -> LintLocation { match expr.kind { - // loops could be using `break` instead of `return` - ExprKind::Block(block, ..) | ExprKind::Loop(block, ..) => { - if let Some(expr) = &block.expr { - expr_match(cx, expr); - } - // only needed in the case of `break` with `;` at the end - else if let Some(stmt) = block.stmts.last() { - if_chain! { - if let StmtKind::Semi(expr, ..) = &stmt.kind; - // make sure it's a break, otherwise we want to skip - if let ExprKind::Break(.., Some(break_expr)) = &expr.kind; - then { - lint(cx, expr.span, break_expr.span, LINT_BREAK); - } - } - } - }, - // use `return` instead of `break` - ExprKind::Break(.., break_expr) => { - if let Some(break_expr) = break_expr { - lint(cx, expr.span, break_expr.span, LINT_BREAK); + ExprKind::Block( + Block { + expr: Some(block_expr), .. + }, + _, + ) => lint_implicit_returns( + cx, + block_expr, + ctxt, + call_site_span.or_else(|| get_call_site(block_expr.span, ctxt)), + ) + .still_parent(call_site_span.is_some()), + + ExprKind::If(_, then_expr, Some(else_expr)) => { + // Both `then_expr` or `else_expr` are required to be blocks in the same context as the `if`. Don't + // bother checking. + let res = lint_implicit_returns(cx, then_expr, ctxt, call_site_span).still_parent(call_site_span.is_some()); + if res.is_parent() { + // The return was added as a parent of this if expression. + return res; } + lint_implicit_returns(cx, else_expr, ctxt, call_site_span).still_parent(call_site_span.is_some()) }, - ExprKind::If(.., if_expr, else_expr) => { - expr_match(cx, if_expr); - if let Some(else_expr) = else_expr { - expr_match(cx, else_expr); + ExprKind::Match(_, arms, _) => { + for arm in arms { + let res = lint_implicit_returns( + cx, + arm.body, + ctxt, + call_site_span.or_else(|| get_call_site(arm.body.span, ctxt)), + ) + .still_parent(call_site_span.is_some()); + if res.is_parent() { + // The return was added as a parent of this match expression. + return res; + } } + LintLocation::Inner }, - ExprKind::Match(.., arms, source) => { - let check_all_arms = match source { - MatchSource::IfLetDesugar { - contains_else_clause: has_else, - } => has_else, - _ => true, - }; - - if check_all_arms { - for arm in arms { - expr_match(cx, arm.body); + + ExprKind::Loop(block, ..) => { + let mut add_return = false; + visit_break_exprs(block, |break_expr, dest, sub_expr| { + if dest.target_id.ok() == Some(expr.hir_id) { + if call_site_span.is_none() && break_expr.span.ctxt() == ctxt { + lint_break(cx, break_expr.span, sub_expr.unwrap().span); + } else { + // the break expression is from a macro call, add a return to the loop + add_return = true; + } + } + }); + if add_return { + #[allow(clippy::option_if_let_else)] + if let Some(span) = call_site_span { + lint_return(cx, span); + LintLocation::Parent + } else { + lint_return(cx, expr.span); + LintLocation::Inner } } else { - expr_match(cx, arms.first().expect("`if let` doesn't have a single arm").body); + LintLocation::Inner } }, - // skip if it already has a return statement - ExprKind::Ret(..) => (), - // make sure it's not a call that panics - ExprKind::Call(expr, ..) => { - if_chain! { - if let ExprKind::Path(qpath) = &expr.kind; - if let Some(path_def_id) = cx.qpath_res(qpath, expr.hir_id).opt_def_id(); - if match_panic_def_id(cx, path_def_id); - then { } - else { - lint(cx, expr.span, expr.span, LINT_RETURN) - } + + // If expressions without an else clause, and blocks without a final expression can only be the final expression + // if they are divergent, or return the unit type. + ExprKind::If(_, _, None) | ExprKind::Block(Block { expr: None, .. }, _) | ExprKind::Ret(_) => { + LintLocation::Inner + }, + + // Any divergent expression doesn't need a return statement. + ExprKind::MethodCall(..) + | ExprKind::Call(..) + | ExprKind::Binary(..) + | ExprKind::Unary(..) + | ExprKind::Index(..) + if cx.typeck_results().expr_ty(expr).is_never() => + { + LintLocation::Inner + }, + + _ => + { + #[allow(clippy::option_if_let_else)] + if let Some(span) = call_site_span { + lint_return(cx, span); + LintLocation::Parent + } else { + lint_return(cx, expr.span); + LintLocation::Inner } }, - // everything else is missing `return` - _ => lint(cx, expr.span, expr.span, LINT_RETURN), } } @@ -129,19 +201,24 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitReturn { fn check_fn( &mut self, cx: &LateContext<'tcx>, - _: FnKind<'tcx>, - _: &'tcx FnDecl<'_>, + kind: FnKind<'tcx>, + decl: &'tcx FnDecl<'_>, body: &'tcx Body<'_>, span: Span, _: HirId, ) { - if span.from_expansion() { + if (!matches!(kind, FnKind::Closure) && matches!(decl.output, FnRetTy::DefaultReturn(_))) + || span.ctxt() != body.value.span.ctxt() + || in_external_macro(cx.sess(), span) + { return; } - let body = cx.tcx.hir().body(body.id()); - if cx.typeck_results().expr_ty(&body.value).is_unit() { + + let res_ty = cx.typeck_results().expr_ty(&body.value); + if res_ty.is_unit() || res_ty.is_never() { return; } - expr_match(cx, &body.value); + + lint_implicit_returns(cx, &body.value, body.value.span.ctxt(), None); } } diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 53180d1f9f54f..2e731c182ece9 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -280,17 +280,17 @@ pub fn snippet_with_context( default: &'a str, applicability: &mut Applicability, ) -> (Cow<'a, str>, bool) { - let outer_span = hygiene::walk_chain(span, outer); - let (span, is_macro_call) = if outer_span.ctxt() == outer { - (outer_span, span.ctxt() != outer) - } else { - // The span is from a macro argument, and the outer context is the macro using the argument - if *applicability != Applicability::Unspecified { - *applicability = Applicability::MaybeIncorrect; - } - // TODO: get the argument span. - (span, false) - }; + let (span, is_macro_call) = walk_span_to_context(span, outer).map_or_else( + || { + // The span is from a macro argument, and the outer context is the macro using the argument + if *applicability != Applicability::Unspecified { + *applicability = Applicability::MaybeIncorrect; + } + // TODO: get the argument span. + (span, false) + }, + |outer_span| (outer_span, span.ctxt() != outer), + ); ( snippet_with_applicability(cx, span, default, applicability), @@ -298,6 +298,15 @@ pub fn snippet_with_context( ) } +/// Walks the span up to the target context, thereby returning the macro call site if the span is +/// inside a macro expansion, or the original span if it is not. Note this will return `None` in the +/// case of the span being in a macro expansion, but the target context is from expanding a macro +/// argument. +pub fn walk_span_to_context(span: Span, outer: SyntaxContext) -> Option { + let outer_span = hygiene::walk_chain(span, outer); + (outer_span.ctxt() == outer).then(|| outer_span) +} + /// Removes block comments from the given `Vec` of lines. /// /// # Examples diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index 5a8c629e3338c..d431bdf34eeee 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -1,7 +1,7 @@ use crate::path_to_local_id; use rustc_hir as hir; -use rustc_hir::intravisit::{self, walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{Arm, Body, Expr, HirId, Stmt}; +use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visitor}; +use rustc_hir::{Arm, Block, Body, Destination, Expr, ExprKind, HirId, Stmt}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; @@ -188,3 +188,54 @@ impl<'v> Visitor<'v> for LocalUsedVisitor<'v> { NestedVisitorMap::OnlyBodies(self.hir) } } + +pub trait Visitable<'tcx> { + fn visit>(self, v: &mut V); +} +impl Visitable<'tcx> for &'tcx Expr<'tcx> { + fn visit>(self, v: &mut V) { + v.visit_expr(self) + } +} +impl Visitable<'tcx> for &'tcx Block<'tcx> { + fn visit>(self, v: &mut V) { + v.visit_block(self) + } +} +impl<'tcx> Visitable<'tcx> for &'tcx Stmt<'tcx> { + fn visit>(self, v: &mut V) { + v.visit_stmt(self) + } +} +impl<'tcx> Visitable<'tcx> for &'tcx Body<'tcx> { + fn visit>(self, v: &mut V) { + v.visit_body(self) + } +} +impl<'tcx> Visitable<'tcx> for &'tcx Arm<'tcx> { + fn visit>(self, v: &mut V) { + v.visit_arm(self) + } +} + +pub fn visit_break_exprs<'tcx>( + node: impl Visitable<'tcx>, + f: impl FnMut(&'tcx Expr<'tcx>, Destination, Option<&'tcx Expr<'tcx>>), +) { + struct V(F); + impl<'tcx, F: FnMut(&'tcx Expr<'tcx>, Destination, Option<&'tcx Expr<'tcx>>)> Visitor<'tcx> for V { + type Map = ErasedMap<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_expr(&mut self, e: &'tcx Expr<'_>) { + if let ExprKind::Break(dest, sub_expr) = e.kind { + self.0(e, dest, sub_expr) + } + walk_expr(self, e); + } + } + + node.visit(&mut V(f)); +} diff --git a/tests/ui/implicit_return.fixed b/tests/ui/implicit_return.fixed index 59f7ad9c10624..c0fc4b926a04b 100644 --- a/tests/ui/implicit_return.fixed +++ b/tests/ui/implicit_return.fixed @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::implicit_return)] -#![allow(clippy::needless_return, unused)] +#![allow(clippy::needless_return, clippy::needless_bool, unused, clippy::never_loop)] fn test_end_of_fn() -> bool { if true { @@ -12,7 +12,6 @@ fn test_end_of_fn() -> bool { return true } -#[allow(clippy::needless_bool)] fn test_if_block() -> bool { if true { return true } else { return false } } @@ -25,7 +24,6 @@ fn test_match(x: bool) -> bool { } } -#[allow(clippy::needless_return)] fn test_match_with_unreachable(x: bool) -> bool { match x { true => return false, @@ -33,14 +31,12 @@ fn test_match_with_unreachable(x: bool) -> bool { } } -#[allow(clippy::never_loop)] fn test_loop() -> bool { loop { return true; } } -#[allow(clippy::never_loop)] fn test_loop_with_block() -> bool { loop { { @@ -49,7 +45,6 @@ fn test_loop_with_block() -> bool { } } -#[allow(clippy::never_loop)] fn test_loop_with_nests() -> bool { loop { if true { @@ -83,15 +78,48 @@ fn test_return_macro() -> String { return format!("test {}", "test") } -fn main() { - let _ = test_end_of_fn(); - let _ = test_if_block(); - let _ = test_match(true); - let _ = test_match_with_unreachable(true); - let _ = test_loop(); - let _ = test_loop_with_block(); - let _ = test_loop_with_nests(); - let _ = test_loop_with_if_let(); - test_closure(); - let _ = test_return_macro(); +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() +} + +fn main() {} diff --git a/tests/ui/implicit_return.rs b/tests/ui/implicit_return.rs index 2c1bc04651508..737ffd5ddce14 100644 --- a/tests/ui/implicit_return.rs +++ b/tests/ui/implicit_return.rs @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::implicit_return)] -#![allow(clippy::needless_return, unused)] +#![allow(clippy::needless_return, clippy::needless_bool, unused, clippy::never_loop)] fn test_end_of_fn() -> bool { if true { @@ -12,7 +12,6 @@ fn test_end_of_fn() -> bool { true } -#[allow(clippy::needless_bool)] fn test_if_block() -> bool { if true { true } else { false } } @@ -25,7 +24,6 @@ fn test_match(x: bool) -> bool { } } -#[allow(clippy::needless_return)] fn test_match_with_unreachable(x: bool) -> bool { match x { true => return false, @@ -33,14 +31,12 @@ fn test_match_with_unreachable(x: bool) -> bool { } } -#[allow(clippy::never_loop)] fn test_loop() -> bool { loop { break true; } } -#[allow(clippy::never_loop)] fn test_loop_with_block() -> bool { loop { { @@ -49,7 +45,6 @@ fn test_loop_with_block() -> bool { } } -#[allow(clippy::never_loop)] fn test_loop_with_nests() -> bool { loop { if true { @@ -83,15 +78,48 @@ fn test_return_macro() -> String { format!("test {}", "test") } -fn main() { - let _ = test_end_of_fn(); - let _ = test_if_block(); - let _ = test_match(true); - let _ = test_match_with_unreachable(true); - let _ = test_loop(); - let _ = test_loop_with_block(); - let _ = test_loop_with_nests(); - let _ = test_loop_with_if_let(); - test_closure(); - let _ = test_return_macro(); +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() +} + +fn main() {} diff --git a/tests/ui/implicit_return.stderr b/tests/ui/implicit_return.stderr index 3608319e5bd2c..632e30cbdc6e0 100644 --- a/tests/ui/implicit_return.stderr +++ b/tests/ui/implicit_return.stderr @@ -7,64 +7,97 @@ LL | true = note: `-D clippy::implicit-return` implied by `-D warnings` error: missing `return` statement - --> $DIR/implicit_return.rs:17:15 + --> $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:17:29 + --> $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:23:17 + --> $DIR/implicit_return.rs:22:17 | LL | true => false, | ^^^^^ help: add `return` as shown: `return false` error: missing `return` statement - --> $DIR/implicit_return.rs:24:20 + --> $DIR/implicit_return.rs:23:20 | LL | false => { true }, | ^^^^ help: add `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:39:9 + --> $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:47: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:56:13 + --> $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:74:18 + --> $DIR/implicit_return.rs:69:18 | LL | let _ = || { true }; | ^^^^ help: add `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:75:16 + --> $DIR/implicit_return.rs:70:16 | LL | let _ = || true; | ^^^^ help: add `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:83:5 + --> $DIR/implicit_return.rs:78:5 | LL | format!("test {}", "test") | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `return` as shown: `return format!("test {}", "test")` -error: aborting due to 11 previous errors +error: missing `return` statement + --> $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:93:13 + | +LL | break true; + | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` + +error: missing `return` statement + --> $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:113:5 + | +LL | / loop { +LL | | m!(true); +LL | | } + | |_____^ + | +help: add `return` as shown + | +LL | return loop { +LL | m!(true); +LL | } + | + +error: aborting due to 15 previous errors From 74cf5f2fc6c121ae0eaaa709c9a303cd82fe9b30 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 2 Apr 2021 17:53:14 -0400 Subject: [PATCH 04/67] Fix `implicit_return` suggestion for async functions --- clippy_lints/src/implicit_return.rs | 11 +++++++- clippy_utils/src/lib.rs | 40 ++++++++++++++++++++++++++--- tests/ui/implicit_return.fixed | 6 +++++ tests/ui/implicit_return.rs | 6 +++++ tests/ui/implicit_return.stderr | 38 +++++++++++++++------------ 5 files changed, 81 insertions(+), 20 deletions(-) diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index 251a73618716f..39612dbf05004 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -1,5 +1,6 @@ use clippy_utils::{ diagnostics::span_lint_and_sugg, + get_async_fn_body, is_async_fn, source::{snippet_with_applicability, snippet_with_context, walk_span_to_context}, visitors::visit_break_exprs, }; @@ -219,6 +220,14 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitReturn { return; } - lint_implicit_returns(cx, &body.value, body.value.span.ctxt(), None); + let expr = if is_async_fn(kind) { + match get_async_fn_body(cx.tcx, body) { + Some(e) => e, + None => return, + } + } else { + &body.value + }; + lint_implicit_returns(cx, expr, expr.span.ctxt(), None); } } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index b7017411927da..6dc96e51a6b1e 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -60,12 +60,12 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{self, walk_expr, ErasedMap, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::LangItem::{ResultErr, ResultOk}; use rustc_hir::{ def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, - ImplItem, ImplItemKind, Item, ItemKind, LangItem, Local, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment, - QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, + ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Node, Param, Pat, PatKind, Path, + PathSegment, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, }; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::exports::Export; @@ -1300,6 +1300,40 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, (conds, blocks) } +/// Checks if the given function kind is an async function. +pub fn is_async_fn(kind: FnKind) -> bool { + matches!(kind, FnKind::ItemFn(_, _, header, _) if header.asyncness == IsAsync::Async) +} + +/// Peels away all the compiler generated code surrounding the body of an async function, +pub fn get_async_fn_body(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> { + if let ExprKind::Call( + _, + &[Expr { + kind: ExprKind::Closure(_, _, body, _, _), + .. + }], + ) = body.value.kind + { + if let ExprKind::Block( + Block { + stmts: [], + expr: + Some(Expr { + kind: ExprKind::DropTemps(expr), + .. + }), + .. + }, + _, + ) = tcx.hir().body(body).value.kind + { + return Some(expr); + } + }; + None +} + // Finds the `#[must_use]` attribute, if any pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> { attrs.iter().find(|a| a.has_name(sym::must_use)) diff --git a/tests/ui/implicit_return.fixed b/tests/ui/implicit_return.fixed index c0fc4b926a04b..7698b88a88c8a 100644 --- a/tests/ui/implicit_return.fixed +++ b/tests/ui/implicit_return.fixed @@ -1,3 +1,4 @@ +// edition:2018 // run-rustfix #![warn(clippy::implicit_return)] @@ -122,4 +123,9 @@ fn divergent_test() -> bool { diverge() } +// issue #6940 +async fn foo() -> bool { + return true +} + fn main() {} diff --git a/tests/ui/implicit_return.rs b/tests/ui/implicit_return.rs index 737ffd5ddce14..45bbc2ec670e0 100644 --- a/tests/ui/implicit_return.rs +++ b/tests/ui/implicit_return.rs @@ -1,3 +1,4 @@ +// edition:2018 // run-rustfix #![warn(clippy::implicit_return)] @@ -122,4 +123,9 @@ fn divergent_test() -> bool { diverge() } +// issue #6940 +async fn foo() -> bool { + true +} + fn main() {} diff --git a/tests/ui/implicit_return.stderr b/tests/ui/implicit_return.stderr index 632e30cbdc6e0..16fe9ed444ff6 100644 --- a/tests/ui/implicit_return.stderr +++ b/tests/ui/implicit_return.stderr @@ -1,5 +1,5 @@ error: missing `return` statement - --> $DIR/implicit_return.rs:12:5 + --> $DIR/implicit_return.rs:13:5 | LL | true | ^^^^ help: add `return` as shown: `return true` @@ -7,85 +7,85 @@ LL | true = note: `-D clippy::implicit-return` implied by `-D warnings` error: missing `return` statement - --> $DIR/implicit_return.rs:16:15 + --> $DIR/implicit_return.rs:17:15 | LL | if true { true } else { false } | ^^^^ help: add `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:16:29 + --> $DIR/implicit_return.rs:17:29 | LL | if true { true } else { false } | ^^^^^ help: add `return` as shown: `return false` error: missing `return` statement - --> $DIR/implicit_return.rs:22:17 + --> $DIR/implicit_return.rs:23:17 | LL | true => false, | ^^^^^ help: add `return` as shown: `return false` error: missing `return` statement - --> $DIR/implicit_return.rs:23:20 + --> $DIR/implicit_return.rs:24:20 | LL | false => { true }, | ^^^^ help: add `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:36:9 + --> $DIR/implicit_return.rs:37:9 | LL | break true; | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:43:13 + --> $DIR/implicit_return.rs:44:13 | LL | break true; | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:51:13 + --> $DIR/implicit_return.rs:52:13 | LL | break true; | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:69:18 + --> $DIR/implicit_return.rs:70:18 | LL | let _ = || { true }; | ^^^^ help: add `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:70:16 + --> $DIR/implicit_return.rs:71:16 | LL | let _ = || true; | ^^^^ help: add `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:78:5 + --> $DIR/implicit_return.rs:79:5 | LL | format!("test {}", "test") | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `return` as shown: `return format!("test {}", "test")` error: missing `return` statement - --> $DIR/implicit_return.rs:87:5 + --> $DIR/implicit_return.rs:88:5 | LL | m!(true, false) | ^^^^^^^^^^^^^^^ help: add `return` as shown: `return m!(true, false)` error: missing `return` statement - --> $DIR/implicit_return.rs:93:13 + --> $DIR/implicit_return.rs:94:13 | LL | break true; | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:98:17 + --> $DIR/implicit_return.rs:99:17 | LL | break 'outer false; | ^^^^^^^^^^^^^^^^^^ help: change `break` to `return` as shown: `return false` error: missing `return` statement - --> $DIR/implicit_return.rs:113:5 + --> $DIR/implicit_return.rs:114:5 | LL | / loop { LL | | m!(true); @@ -99,5 +99,11 @@ LL | m!(true); LL | } | -error: aborting due to 15 previous errors +error: missing `return` statement + --> $DIR/implicit_return.rs:128:5 + | +LL | true + | ^^^^ help: add `return` as shown: `return true` + +error: aborting due to 16 previous errors From ef9ad806179665cce9c5813db369038d84291fd8 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 13 Apr 2021 20:55:12 -0400 Subject: [PATCH 05/67] Add examples to better explain `walk_span_to_context` --- clippy_utils/src/source.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 2e731c182ece9..4d49b43bde9ec 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -302,6 +302,28 @@ pub fn snippet_with_context( /// inside a macro expansion, or the original span if it is not. Note this will return `None` in the /// case of the span being in a macro expansion, but the target context is from expanding a macro /// argument. +/// +/// Given the following +/// +/// ```rust,ignore +/// macro_rules! m { ($e:expr) => { f($e) }; } +/// g(m!(0)) +/// ``` +/// +/// If called with a span of the call to `f` and a context of the call to `g` this will return a +/// span containing `m!(0)`. However, if called with a span of the literal `0` this will give a span +/// containing `0` as the context is the same as the outer context. +/// +/// This will traverse through multiple macro calls. Given the following: +/// +/// ```rust,ignore +/// macro_rules! m { ($e:expr) => { n!($e, 0) }; } +/// macro_rules! n { ($e:expr, $f:expr) => { f($e, $f) }; } +/// g(m!(0)) +/// ``` +/// +/// If called with a span of the call to `f` and a context of the call to `g` this will return a +/// span containing `m!(0)`. pub fn walk_span_to_context(span: Span, outer: SyntaxContext) -> Option { let outer_span = hygiene::walk_chain(span, outer); (outer_span.ctxt() == outer).then(|| outer_span) From 3d793f3111845c8d5836310387b65622ea86b01f Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 22 Apr 2021 09:11:36 -0400 Subject: [PATCH 06/67] Minor cleanup of `implicit_return` --- clippy_lints/src/implicit_return.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index 39612dbf05004..30174fa2100db 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -70,6 +70,7 @@ fn lint_break(cx: &LateContext<'_>, break_span: Span, expr_span: Span) { ) } +#[derive(Clone, Copy, PartialEq, Eq)] enum LintLocation { /// The lint was applied to a parent expression. Parent, @@ -81,8 +82,8 @@ impl LintLocation { if b { self } else { Self::Inner } } - fn is_parent(&self) -> bool { - matches!(*self, Self::Parent) + fn is_parent(self) -> bool { + self == Self::Parent } } From f801d026f501bb19620f53f2a4d03ec990614cab Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 25 Apr 2021 17:05:48 +0200 Subject: [PATCH 07/67] fix clippy --- clippy_lints/src/missing_const_for_fn.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 93b7a897405ae..60425ff853b7e 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -139,7 +139,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { let mir = cx.tcx.optimized_mir(def_id); if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv.as_ref()) { - if rustc_mir::const_eval::is_min_const_fn(cx.tcx, def_id.to_def_id()) { + if cx.tcx.is_const_fn_raw(def_id.to_def_id()) { cx.tcx.sess.span_err(span, &err); } } else { From ae72f1adb9cbf16141f880e9e955723a5fdabf00 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Tue, 27 Apr 2021 16:55:11 +0200 Subject: [PATCH 08/67] Merge commit '7c7683c8efe447b251d6c5ca6cce51233060f6e8' into clippyup --- clippy_lints/src/casts/ptr_as_ptr.rs | 6 +- clippy_lints/src/checked_conversions.rs | 6 +- clippy_lints/src/from_over_into.rs | 6 +- clippy_lints/src/if_then_some_else_none.rs | 6 +- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/loops/single_element_loop.rs | 8 ++- clippy_lints/src/manual_non_exhaustive.rs | 6 +- clippy_lints/src/manual_strip.rs | 6 +- clippy_lints/src/manual_unwrap_or.rs | 11 ++- clippy_lints/src/matches.rs | 9 ++- clippy_lints/src/mem_replace.rs | 6 +- .../src/methods/cloned_instead_of_copied.rs | 21 ++++-- clippy_lints/src/methods/filter_map_next.rs | 6 +- clippy_lints/src/methods/map_unwrap_or.rs | 11 ++- clippy_lints/src/methods/mod.rs | 2 +- .../src/methods/option_as_ref_deref.rs | 6 +- clippy_lints/src/methods/utils.rs | 4 +- clippy_lints/src/missing_const_for_fn.rs | 6 +- clippy_lints/src/needless_question_mark.rs | 42 +++-------- clippy_lints/src/ranges.rs | 6 +- clippy_lints/src/redundant_field_names.rs | 6 +- .../src/redundant_static_lifetimes.rs | 6 +- .../src/single_component_path_imports.rs | 36 ++++++++-- clippy_lints/src/unnested_or_patterns.rs | 17 ++--- clippy_lints/src/unused_io_amount.rs | 20 ++++-- clippy_lints/src/use_self.rs | 11 +-- clippy_lints/src/utils/conf.rs | 2 +- clippy_utils/src/lib.rs | 1 + clippy_utils/src/msrvs.rs | 29 ++++++++ clippy_utils/src/qualify_min_const_fn.rs | 10 ++- doc/adding_lints.md | 24 ++++--- lintcheck/src/main.rs | 7 ++ tests/ui/crashes/ice-7126.rs | 14 ++++ tests/ui/iter_cloned_collect.fixed | 4 ++ tests/ui/iter_cloned_collect.rs | 4 ++ tests/ui/iter_cloned_collect.stderr | 8 ++- tests/ui/manual_unwrap_or.fixed | 12 ++++ tests/ui/manual_unwrap_or.rs | 15 ++++ tests/ui/manual_unwrap_or.stderr | 12 +++- tests/ui/min_rust_version_attr.rs | 4 ++ tests/ui/min_rust_version_attr.stderr | 8 +-- tests/ui/needless_question_mark.fixed | 72 ------------------- tests/ui/needless_question_mark.rs | 72 ------------------- tests/ui/needless_question_mark.stderr | 22 +----- .../single_component_path_imports_macro.fixed | 21 ++++++ .../ui/single_component_path_imports_macro.rs | 21 ++++++ ...single_component_path_imports_macro.stderr | 10 +++ tests/ui/single_element_loop.fixed | 5 ++ tests/ui/single_element_loop.rs | 4 ++ tests/ui/single_element_loop.stderr | 18 ++++- tests/ui/unused_io_amount.rs | 41 ++++++++++- tests/ui/unused_io_amount.stderr | 30 +++++++- 52 files changed, 416 insertions(+), 326 deletions(-) create mode 100644 clippy_utils/src/msrvs.rs create mode 100644 tests/ui/crashes/ice-7126.rs create mode 100644 tests/ui/single_component_path_imports_macro.fixed create mode 100644 tests/ui/single_component_path_imports_macro.rs create mode 100644 tests/ui/single_component_path_imports_macro.stderr diff --git a/clippy_lints/src/casts/ptr_as_ptr.rs b/clippy_lints/src/casts/ptr_as_ptr.rs index 9113e5a0920a2..3132d3a5cf097 100644 --- a/clippy_lints/src/casts/ptr_as_ptr.rs +++ b/clippy_lints/src/casts/ptr_as_ptr.rs @@ -1,8 +1,8 @@ use std::borrow::Cow; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::meets_msrv; use clippy_utils::sugg::Sugg; +use clippy_utils::{meets_msrv, msrvs}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Mutability, TyKind}; @@ -12,10 +12,8 @@ use rustc_semver::RustcVersion; use super::PTR_AS_PTR; -const PTR_AS_PTR_MSRV: RustcVersion = RustcVersion::new(1, 38, 0); - pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: &Option) { - if !meets_msrv(msrv.as_ref(), &PTR_AS_PTR_MSRV) { + if !meets_msrv(msrv.as_ref(), &msrvs::POINTER_CAST) { return; } diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 6a2666bc6c011..8d3123e1ec8ee 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{meets_msrv, SpanlessEq}; +use clippy_utils::{meets_msrv, msrvs, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -12,8 +12,6 @@ use rustc_middle::lint::in_external_macro; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; -const CHECKED_CONVERSIONS_MSRV: RustcVersion = RustcVersion::new(1, 34, 0); - declare_clippy_lint! { /// **What it does:** Checks for explicit bounds checking when casting. /// @@ -58,7 +56,7 @@ impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]); impl<'tcx> LateLintPass<'tcx> for CheckedConversions { fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) { - if !meets_msrv(self.msrv.as_ref(), &CHECKED_CONVERSIONS_MSRV) { + if !meets_msrv(self.msrv.as_ref(), &msrvs::TRY_FROM) { return; } diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 5e2baba894349..3560672a74812 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -1,14 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::paths::INTO; -use clippy_utils::{match_def_path, meets_msrv}; +use clippy_utils::{match_def_path, meets_msrv, msrvs}; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; -const FROM_OVER_INTO_MSRV: RustcVersion = RustcVersion::new(1, 41, 0); - declare_clippy_lint! { /// **What it does:** Searches for implementations of the `Into<..>` trait and suggests to implement `From<..>` instead. /// @@ -57,7 +55,7 @@ impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]); impl LateLintPass<'_> for FromOverInto { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if !meets_msrv(self.msrv.as_ref(), &FROM_OVER_INTO_MSRV) { + if !meets_msrv(self.msrv.as_ref(), &msrvs::RE_REBALANCING_COHERENCE) { return; } diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 85c95f1151f84..eadcd0867a880 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet_with_macro_callsite; -use clippy_utils::{is_else_clause, is_lang_ctor, meets_msrv}; +use clippy_utils::{is_else_clause, is_lang_ctor, meets_msrv, msrvs}; use if_chain::if_chain; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind}; @@ -9,8 +9,6 @@ use rustc_middle::lint::in_external_macro; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; -const IF_THEN_SOME_ELSE_NONE_MSRV: RustcVersion = RustcVersion::new(1, 50, 0); - declare_clippy_lint! { /// **What it does:** Checks for if-else that could be written to `bool::then`. /// @@ -59,7 +57,7 @@ impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]); impl LateLintPass<'_> for IfThenSomeElseNone { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv.as_ref(), &IF_THEN_SOME_ELSE_NONE_MSRV) { + if !meets_msrv(self.msrv.as_ref(), &msrvs::BOOL_THEN) { return; } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d6134e178ca17..757d7669bd806 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1077,7 +1077,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box from_over_into::FromOverInto::new(msrv)); store.register_late_pass(move || box use_self::UseSelf::new(msrv)); store.register_late_pass(move || box missing_const_for_fn::MissingConstForFn::new(msrv)); - store.register_late_pass(move || box needless_question_mark::NeedlessQuestionMark::new(msrv)); + store.register_late_pass(move || box needless_question_mark::NeedlessQuestionMark); store.register_late_pass(move || box casts::Casts::new(msrv)); store.register_early_pass(move || box unnested_or_patterns::UnnestedOrPatterns::new(msrv)); diff --git a/clippy_lints/src/loops/single_element_loop.rs b/clippy_lints/src/loops/single_element_loop.rs index fc067e81bcafb..0fd09ff7197ae 100644 --- a/clippy_lints/src/loops/single_element_loop.rs +++ b/clippy_lints/src/loops/single_element_loop.rs @@ -14,8 +14,14 @@ pub(super) fn check<'tcx>( body: &'tcx Expr<'_>, expr: &'tcx Expr<'_>, ) { + let arg_expr = match arg.kind { + ExprKind::AddrOf(BorrowKind::Ref, _, ref_arg) => ref_arg, + ExprKind::MethodCall(method, _, args, _) if args.len() == 1 && method.ident.name == rustc_span::sym::iter => { + &args[0] + }, + _ => return, + }; if_chain! { - if let ExprKind::AddrOf(BorrowKind::Ref, _, arg_expr) = arg.kind; if let PatKind::Binding(.., target, _) = pat.kind; if let ExprKind::Array([arg_expression]) = arg_expr.kind; if let ExprKind::Path(ref list_item) = arg_expression.kind; diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index dc19805b50abd..54f714b54b657 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -1,7 +1,7 @@ use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::meets_msrv; use clippy_utils::source::snippet_opt; +use clippy_utils::{meets_msrv, msrvs}; use if_chain::if_chain; use rustc_ast::ast::{FieldDef, Item, ItemKind, Variant, VariantData, VisibilityKind}; use rustc_errors::Applicability; @@ -10,8 +10,6 @@ use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; -const MANUAL_NON_EXHAUSTIVE_MSRV: RustcVersion = RustcVersion::new(1, 40, 0); - declare_clippy_lint! { /// **What it does:** Checks for manual implementations of the non-exhaustive pattern. /// @@ -76,7 +74,7 @@ impl_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]); impl EarlyLintPass for ManualNonExhaustive { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if !meets_msrv(self.msrv.as_ref(), &MANUAL_NON_EXHAUSTIVE_MSRV) { + if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) { return; } diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index dfa464ddb81ac..23428524dee97 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -2,7 +2,7 @@ use crate::consts::{constant, Constant}; use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::usage::mutated_variables; -use clippy_utils::{eq_expr_value, higher, match_def_path, meets_msrv, paths}; +use clippy_utils::{eq_expr_value, higher, match_def_path, meets_msrv, msrvs, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_hir::def::Res; @@ -17,8 +17,6 @@ use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Spanned; use rustc_span::Span; -const MANUAL_STRIP_MSRV: RustcVersion = RustcVersion::new(1, 45, 0); - declare_clippy_lint! { /// **What it does:** /// Suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing using @@ -74,7 +72,7 @@ enum StripKind { impl<'tcx> LateLintPass<'tcx> for ManualStrip { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv.as_ref(), &MANUAL_STRIP_MSRV) { + if !meets_msrv(self.msrv.as_ref(), &msrvs::STR_STRIP_PREFIX) { return; } diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index 65baa2552ccc6..520162559e50f 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -112,6 +112,15 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { then { let reindented_or_body = reindent_multiline(or_body_snippet.into(), true, Some(indent)); + + let suggestion = if scrutinee.span.from_expansion() { + // we don't want parenthesis around macro, e.g. `(some_macro!()).unwrap_or(0)` + sugg::Sugg::hir_with_macro_callsite(cx, scrutinee, "..") + } + else { + sugg::Sugg::hir(cx, scrutinee, "..").maybe_par() + }; + span_lint_and_sugg( cx, MANUAL_UNWRAP_OR, expr.span, @@ -119,7 +128,7 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { "replace with", format!( "{}.unwrap_or({})", - sugg::Sugg::hir(cx, scrutinee, "..").maybe_par(), + suggestion, reindented_or_body, ), Applicability::MachineApplicable, diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 44b4eb2903531..13b2a834b0a96 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -7,8 +7,9 @@ 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::LocalUsedVisitor; use clippy_utils::{ - get_parent_expr, in_macro, is_allowed, is_expn_of, is_lang_ctor, is_refutable, is_wild, meets_msrv, path_to_local, - path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks, strip_pat_refs, + get_parent_expr, in_macro, is_allowed, is_expn_of, is_lang_ctor, is_refutable, 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 if_chain::if_chain; @@ -578,8 +579,6 @@ impl_lint_pass!(Matches => [ MATCH_SAME_ARMS, ]); -const MATCH_LIKE_MATCHES_MACRO_MSRV: RustcVersion = RustcVersion::new(1, 42, 0); - 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) { @@ -588,7 +587,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { redundant_pattern_match::check(cx, expr); - if meets_msrv(self.msrv.as_ref(), &MATCH_LIKE_MATCHES_MACRO_MSRV) { + if meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO) { if !check_match_like_matches(cx, expr) { lint_match_arms(cx, expr); } diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index ec60bffe95555..183daee361774 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_applicability}; -use clippy_utils::{in_macro, is_diag_trait_item, is_lang_ctor, match_def_path, meets_msrv, paths}; +use clippy_utils::{in_macro, is_diag_trait_item, is_lang_ctor, match_def_path, meets_msrv, msrvs, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; @@ -256,8 +256,6 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr< } } -const MEM_REPLACE_WITH_DEFAULT_MSRV: RustcVersion = RustcVersion::new(1, 40, 0); - pub struct MemReplace { msrv: Option, } @@ -281,7 +279,7 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace { then { check_replace_option_with_none(cx, src, dest, expr.span); check_replace_with_uninit(cx, src, dest, expr.span); - if meets_msrv(self.msrv.as_ref(), &MEM_REPLACE_WITH_DEFAULT_MSRV) { + if meets_msrv(self.msrv.as_ref(), &msrvs::MEM_TAKE) { check_replace_with_default(cx, src, dest, expr.span); } } diff --git a/clippy_lints/src/methods/cloned_instead_of_copied.rs b/clippy_lints/src/methods/cloned_instead_of_copied.rs index ba97ab3900ca4..ecec6da3aa0f6 100644 --- a/clippy_lints/src/methods/cloned_instead_of_copied.rs +++ b/clippy_lints/src/methods/cloned_instead_of_copied.rs @@ -1,23 +1,30 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; use clippy_utils::ty::{get_iterator_item_ty, is_copy}; +use clippy_utils::{is_trait_method, meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty; +use rustc_semver::RustcVersion; use rustc_span::{sym, Span}; use super::CLONED_INSTEAD_OF_COPIED; -pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span) { +pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: Option<&RustcVersion>) { let recv_ty = cx.typeck_results().expr_ty_adjusted(recv); let inner_ty = match recv_ty.kind() { // `Option` -> `T` - ty::Adt(adt, subst) if cx.tcx.is_diagnostic_item(sym::option_type, adt.did) => subst.type_at(0), - _ if is_trait_method(cx, expr, sym::Iterator) => match get_iterator_item_ty(cx, recv_ty) { - // ::Item - Some(ty) => ty, - _ => return, + ty::Adt(adt, subst) + if cx.tcx.is_diagnostic_item(sym::option_type, adt.did) && meets_msrv(msrv, &msrvs::OPTION_COPIED) => + { + subst.type_at(0) + }, + _ if is_trait_method(cx, expr, sym::Iterator) && meets_msrv(msrv, &msrvs::ITERATOR_COPIED) => { + match get_iterator_item_ty(cx, recv_ty) { + // ::Item + Some(ty) => ty, + _ => return, + } }, _ => return, }; diff --git a/clippy_lints/src/methods/filter_map_next.rs b/clippy_lints/src/methods/filter_map_next.rs index 2b19e4ee8c055..f0d69a1f42e7b 100644 --- a/clippy_lints/src/methods/filter_map_next.rs +++ b/clippy_lints/src/methods/filter_map_next.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::snippet; -use clippy_utils::{is_trait_method, meets_msrv}; +use clippy_utils::{is_trait_method, meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -9,8 +9,6 @@ use rustc_span::sym; use super::FILTER_MAP_NEXT; -const FILTER_MAP_NEXT_MSRV: RustcVersion = RustcVersion::new(1, 30, 0); - pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, @@ -19,7 +17,7 @@ pub(super) fn check<'tcx>( msrv: Option<&RustcVersion>, ) { if is_trait_method(cx, expr, sym::Iterator) { - if !meets_msrv(msrv, &FILTER_MAP_NEXT_MSRV) { + if !meets_msrv(msrv, &msrvs::ITERATOR_FIND_MAP) { return; } diff --git a/clippy_lints/src/methods/map_unwrap_or.rs b/clippy_lints/src/methods/map_unwrap_or.rs index 4330fea727b3a..4d8365fcda126 100644 --- a/clippy_lints/src/methods/map_unwrap_or.rs +++ b/clippy_lints/src/methods/map_unwrap_or.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; -use clippy_utils::meets_msrv; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::mutated_variables; +use clippy_utils::{meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -11,8 +11,6 @@ use rustc_span::symbol::sym; use super::MAP_UNWRAP_OR; -const MAP_UNWRAP_OR_MSRV: RustcVersion = RustcVersion::new(1, 41, 0); - /// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s /// Return true if lint triggered pub(super) fn check<'tcx>( @@ -23,13 +21,14 @@ pub(super) fn check<'tcx>( unwrap_arg: &'tcx hir::Expr<'_>, msrv: Option<&RustcVersion>, ) -> bool { - if !meets_msrv(msrv, &MAP_UNWRAP_OR_MSRV) { - return false; - } // lint if the caller of `map()` is an `Option` let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::option_type); let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::result_type); + if is_result && !meets_msrv(msrv, &msrvs::RESULT_MAP_OR_ELSE) { + return false; + } + if is_option || is_result { // Don't make a suggestion that may fail to compile due to mutably borrowing // the same variable twice. diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c2cd3011d1493..e15dbb899b36a 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1959,7 +1959,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv), ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), - ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span), + ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, msrv), ("collect", []) => match method_call!(recv) { Some(("cloned", [recv2], _)) => iter_cloned_collect::check(cx, expr, recv2), Some(("map", [m_recv, m_arg], _)) => { diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index 7e9c8fa829dec..5a57135038fdc 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{match_def_path, meets_msrv, path_to_local_id, paths, remove_blocks}; +use clippy_utils::{match_def_path, meets_msrv, msrvs, path_to_local_id, paths, remove_blocks}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -12,8 +12,6 @@ use rustc_span::sym; use super::OPTION_AS_REF_DEREF; -const OPTION_AS_REF_DEREF_MSRV: RustcVersion = RustcVersion::new(1, 40, 0); - /// lint use of `_.as_ref().map(Deref::deref)` for `Option`s pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, @@ -23,7 +21,7 @@ pub(super) fn check<'tcx>( is_mut: bool, msrv: Option<&RustcVersion>, ) { - if !meets_msrv(msrv, &OPTION_AS_REF_DEREF_MSRV) { + if !meets_msrv(msrv, &msrvs::OPTION_AS_DEREF) { return; } diff --git a/clippy_lints/src/methods/utils.rs b/clippy_lints/src/methods/utils.rs index f6bf37e08b966..0daea47816a51 100644 --- a/clippy_lints/src/methods/utils.rs +++ b/clippy_lints/src/methods/utils.rs @@ -18,9 +18,7 @@ pub(super) fn derefs_to_slice<'tcx>( ty::Slice(_) => true, ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()), ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::vec_type), - ty::Array(_, size) => size - .try_eval_usize(cx.tcx, cx.param_env) - .map_or(false, |size| size < 32), + ty::Array(_, size) => size.try_eval_usize(cx.tcx, cx.param_env).is_some(), ty::Ref(_, inner, _) => may_slice(cx, inner), _ => false, } diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 60425ff853b7e..59cbc481ed42e 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::qualify_min_const_fn::is_min_const_fn; use clippy_utils::ty::has_drop; -use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, meets_msrv, trait_ref_of_method}; +use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, meets_msrv, msrvs, trait_ref_of_method}; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId}; @@ -12,8 +12,6 @@ use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; use rustc_typeck::hir_ty_to_ty; -const MISSING_CONST_FOR_FN_MSRV: RustcVersion = RustcVersion::new(1, 37, 0); - declare_clippy_lint! { /// **What it does:** /// @@ -97,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { span: Span, hir_id: HirId, ) { - if !meets_msrv(self.msrv.as_ref(), &MISSING_CONST_FOR_FN_MSRV) { + if !meets_msrv(self.msrv.as_ref(), &msrvs::CONST_IF_MATCH) { return; } diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index cfe7ae6630e04..7b156a8c49dd9 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -1,15 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_lang_ctor; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{differing_macro_contexts, meets_msrv}; +use clippy_utils::{differing_macro_contexts, is_lang_ctor}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionSome, ResultOk}; use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_semver::RustcVersion; -use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; declare_clippy_lint! { @@ -63,21 +61,7 @@ declare_clippy_lint! { "Suggest `value.inner_option` instead of `Some(value.inner_option?)`. The same goes for `Result`." } -const NEEDLESS_QUESTION_MARK_RESULT_MSRV: RustcVersion = RustcVersion::new(1, 13, 0); -const NEEDLESS_QUESTION_MARK_OPTION_MSRV: RustcVersion = RustcVersion::new(1, 22, 0); - -pub struct NeedlessQuestionMark { - msrv: Option, -} - -impl NeedlessQuestionMark { - #[must_use] - pub fn new(msrv: Option) -> Self { - Self { msrv } - } -} - -impl_lint_pass!(NeedlessQuestionMark => [NEEDLESS_QUESTION_MARK]); +declare_lint_pass!(NeedlessQuestionMark => [NEEDLESS_QUESTION_MARK]); #[derive(Debug)] enum SomeOkCall<'a> { @@ -111,7 +95,7 @@ impl LateLintPass<'_> for NeedlessQuestionMark { _ => return, }; - if let Some(ok_some_call) = is_some_or_ok_call(self, cx, e) { + if let Some(ok_some_call) = is_some_or_ok_call(cx, e) { emit_lint(cx, &ok_some_call); } } @@ -127,14 +111,12 @@ impl LateLintPass<'_> for NeedlessQuestionMark { if_chain! { if let Some(expr) = expr_opt; - if let Some(ok_some_call) = is_some_or_ok_call(self, cx, expr); + if let Some(ok_some_call) = is_some_or_ok_call(cx, expr); then { emit_lint(cx, &ok_some_call); } }; } - - extract_msrv_attr!(LateContext); } fn emit_lint(cx: &LateContext<'_>, expr: &SomeOkCall<'_>) { @@ -153,11 +135,7 @@ fn emit_lint(cx: &LateContext<'_>, expr: &SomeOkCall<'_>) { ); } -fn is_some_or_ok_call<'a>( - nqml: &NeedlessQuestionMark, - cx: &'a LateContext<'_>, - expr: &'a Expr<'_>, -) -> Option> { +fn is_some_or_ok_call<'a>(cx: &'a LateContext<'_>, expr: &'a Expr<'_>) -> Option> { if_chain! { // Check outer expression matches CALL_IDENT(ARGUMENT) format if let ExprKind::Call(path, args) = &expr.kind; @@ -188,8 +166,7 @@ fn is_some_or_ok_call<'a>( let inner_is_some = is_type_diagnostic_item(cx, inner_ty, sym::option_type); // Check for Option MSRV - let meets_option_msrv = meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_OPTION_MSRV); - if outer_is_some && inner_is_some && meets_option_msrv { + if outer_is_some && inner_is_some { return Some(SomeOkCall::SomeCall(expr, inner_expr)); } @@ -202,8 +179,7 @@ fn is_some_or_ok_call<'a>( let does_not_call_from = !has_implicit_error_from(cx, expr, inner_expr); // Must meet Result MSRV - let meets_result_msrv = meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_RESULT_MSRV); - if outer_is_result && inner_is_result && does_not_call_from && meets_result_msrv { + if outer_is_result && inner_is_result && does_not_call_from { return Some(SomeOkCall::OkCall(expr, inner_expr)); } } diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 1c3c125e57913..7169f96eaf1f3 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -2,7 +2,7 @@ use crate::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; use clippy_utils::sugg::Sugg; -use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, single_segment_path}; +use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, msrvs, single_segment_path}; use clippy_utils::{higher, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast::RangeLimits; @@ -159,8 +159,6 @@ declare_clippy_lint! { "manually reimplementing {`Range`, `RangeInclusive`}`::contains`" } -const MANUAL_RANGE_CONTAINS_MSRV: RustcVersion = RustcVersion::new(1, 35, 0); - pub struct Ranges { msrv: Option, } @@ -187,7 +185,7 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { check_range_zip_with_len(cx, path, args, expr.span); }, ExprKind::Binary(ref op, l, r) => { - if meets_msrv(self.msrv.as_ref(), &MANUAL_RANGE_CONTAINS_MSRV) { + if meets_msrv(self.msrv.as_ref(), &msrvs::RANGE_CONTAINS) { check_possible_range_contains(cx, op.node, l, r, expr); } }, diff --git a/clippy_lints/src/redundant_field_names.rs b/clippy_lints/src/redundant_field_names.rs index abebd4227975e..d5ee8d3468deb 100644 --- a/clippy_lints/src/redundant_field_names.rs +++ b/clippy_lints/src/redundant_field_names.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::meets_msrv; +use clippy_utils::{meets_msrv, msrvs}; use rustc_ast::ast::{Expr, ExprKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -7,8 +7,6 @@ use rustc_middle::lint::in_external_macro; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; -const REDUNDANT_FIELD_NAMES_MSRV: RustcVersion = RustcVersion::new(1, 17, 0); - declare_clippy_lint! { /// **What it does:** Checks for fields in struct literals where shorthands /// could be used. @@ -52,7 +50,7 @@ impl_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]); impl EarlyLintPass for RedundantFieldNames { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if !meets_msrv(self.msrv.as_ref(), &REDUNDANT_FIELD_NAMES_MSRV) { + if !meets_msrv(self.msrv.as_ref(), &msrvs::FIELD_INIT_SHORTHAND) { return; } diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index 32b57698ec54d..48107d9c037db 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -1,14 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::meets_msrv; use clippy_utils::source::snippet; +use clippy_utils::{meets_msrv, msrvs}; use rustc_ast::ast::{Item, ItemKind, Ty, TyKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; -const REDUNDANT_STATIC_LIFETIMES_MSRV: RustcVersion = RustcVersion::new(1, 17, 0); - declare_clippy_lint! { /// **What it does:** Checks for constants and statics with an explicit `'static` lifetime. /// @@ -100,7 +98,7 @@ impl RedundantStaticLifetimes { impl EarlyLintPass for RedundantStaticLifetimes { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if !meets_msrv(self.msrv.as_ref(), &REDUNDANT_STATIC_LIFETIMES_MSRV) { + if !meets_msrv(self.msrv.as_ref(), &msrvs::STATIC_IN_CONST) { return; } diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index 6104103580e98..a45bb1023899d 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::in_macro; -use rustc_ast::{ptr::P, Crate, Item, ItemKind, ModKind, UseTreeKind}; +use rustc_ast::{ptr::P, Crate, Item, ItemKind, MacroDef, ModKind, UseTreeKind, VisibilityKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -60,8 +60,21 @@ fn check_mod(cx: &EarlyContext<'_>, items: &[P]) { // ``` let mut single_use_usages = Vec::new(); + // keep track of macros defined in the module as we don't want it to trigger on this (#7106) + // ```rust,ignore + // macro_rules! foo { () => {} }; + // pub(crate) use foo; + // ``` + let mut macros = Vec::new(); + for item in items { - track_uses(cx, &item, &mut imports_reused_with_self, &mut single_use_usages); + track_uses( + cx, + &item, + &mut imports_reused_with_self, + &mut single_use_usages, + &mut macros, + ); } for single_use in &single_use_usages { @@ -96,6 +109,7 @@ fn track_uses( item: &Item, imports_reused_with_self: &mut Vec, single_use_usages: &mut Vec<(Symbol, Span, bool)>, + macros: &mut Vec, ) { if in_macro(item.span) || item.vis.kind.is_pub() { return; @@ -105,14 +119,22 @@ fn track_uses( ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => { check_mod(cx, &items); }, + ItemKind::MacroDef(MacroDef { macro_rules: true, .. }) => { + macros.push(item.ident.name); + }, ItemKind::Use(use_tree) => { let segments = &use_tree.prefix.segments; + let should_report = + |name: &Symbol| !macros.contains(name) || matches!(item.vis.kind, VisibilityKind::Inherited); + // keep track of `use some_module;` usages if segments.len() == 1 { if let UseTreeKind::Simple(None, _, _) = use_tree.kind { - let ident = &segments[0].ident; - single_use_usages.push((ident.name, item.span, true)); + let name = segments[0].ident.name; + if should_report(&name) { + single_use_usages.push((name, item.span, true)); + } } return; } @@ -124,8 +146,10 @@ fn track_uses( let segments = &tree.0.prefix.segments; if segments.len() == 1 { if let UseTreeKind::Simple(None, _, _) = tree.0.kind { - let ident = &segments[0].ident; - single_use_usages.push((ident.name, tree.0.span, false)); + let name = segments[0].ident.name; + if should_report(&name) { + single_use_usages.push((name, tree.0.span, false)); + } } } } diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index 9376a2cf66a90..3e985fa72b8fe 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -1,11 +1,8 @@ #![allow(clippy::wildcard_imports, clippy::enum_glob_use)] +use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::over; -use clippy_utils::{ - ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path}, - meets_msrv, -}; +use clippy_utils::{meets_msrv, msrvs, over}; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; use rustc_ast::{self as ast, Pat, PatKind, PatKind::*, DUMMY_NODE_ID}; @@ -54,8 +51,6 @@ declare_clippy_lint! { "unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`" } -const UNNESTED_OR_PATTERNS_MSRV: RustcVersion = RustcVersion::new(1, 53, 0); - #[derive(Clone, Copy)] pub struct UnnestedOrPatterns { msrv: Option, @@ -72,13 +67,13 @@ impl_lint_pass!(UnnestedOrPatterns => [UNNESTED_OR_PATTERNS]); impl EarlyLintPass for UnnestedOrPatterns { fn check_arm(&mut self, cx: &EarlyContext<'_>, a: &ast::Arm) { - if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) { + if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) { lint_unnested_or_patterns(cx, &a.pat); } } fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { - if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) { + if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) { if let ast::ExprKind::Let(pat, _) = &e.kind { lint_unnested_or_patterns(cx, pat); } @@ -86,13 +81,13 @@ impl EarlyLintPass for UnnestedOrPatterns { } fn check_param(&mut self, cx: &EarlyContext<'_>, p: &ast::Param) { - if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) { + if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) { lint_unnested_or_patterns(cx, &p.pat); } } fn check_local(&mut self, cx: &EarlyContext<'_>, l: &ast::Local) { - if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) { + if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) { lint_unnested_or_patterns(cx, &l.pat); } } diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 5e8e530f480fb..3387f35bac3d4 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -47,25 +47,35 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { func.kind, hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryIntoResult, _)) ) { - check_method_call(cx, &args[0], expr); + check_map_error(cx, &args[0], expr); } } else { - check_method_call(cx, res, expr); + check_map_error(cx, res, expr); } }, - hir::ExprKind::MethodCall(path, _, args, _) => match &*path.ident.as_str() { "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" => { - check_method_call(cx, &args[0], expr); + check_map_error(cx, &args[0], expr); }, _ => (), }, - _ => (), } } } +fn check_map_error(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) { + let mut call = call; + while let hir::ExprKind::MethodCall(ref path, _, ref args, _) = call.kind { + if matches!(&*path.ident.as_str(), "or" | "or_else" | "ok") { + call = &args[0]; + } else { + break; + } + } + check_method_call(cx, call, expr); +} + fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) { if let hir::ExprKind::MethodCall(path, _, _, _) = call.kind { let symbol = &*path.ident.as_str(); diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index c6a3c58a9a2a4..aa4d16633ff80 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; -use clippy_utils::{in_macro, meets_msrv}; +use clippy_utils::{in_macro, meets_msrv, msrvs}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -62,8 +62,6 @@ pub struct UseSelf { stack: Vec, } -const USE_SELF_MSRV: RustcVersion = RustcVersion::new(1, 37, 0); - impl UseSelf { #[must_use] pub fn new(msrv: Option) -> Self { @@ -236,7 +234,10 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { } fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) { - if in_macro(hir_ty.span) | in_impl(cx, hir_ty) | !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) { + if in_macro(hir_ty.span) + || in_impl(cx, hir_ty) + || !meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS) + { return; } @@ -288,7 +289,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { } } - if in_macro(expr.span) | !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) { + if in_macro(expr.span) || !meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS) { return; } diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 147f823491d6d..d56855a71c159 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -106,7 +106,7 @@ macro_rules! define_Conf { pub use self::helpers::Conf; define_Conf! { - /// Lint: 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, NEEDLESS_QUESTION_MARK, PTR_AS_PTR. The minimum rust version that the project supports + /// Lint: 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. The minimum rust version that the project supports (msrv, "msrv": Option, None), /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses (blacklisted_names, "blacklisted_names": Vec, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()), diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 9564432ee48e1..cd85c487798d9 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -38,6 +38,7 @@ 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; diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs new file mode 100644 index 0000000000000..00df04c0144c1 --- /dev/null +++ b/clippy_utils/src/msrvs.rs @@ -0,0 +1,29 @@ +use rustc_semver::RustcVersion; + +macro_rules! msrv_aliases { + ($($major:literal,$minor:literal,$patch:literal { + $($name:ident),* $(,)? + })*) => { + $($( + pub const $name: RustcVersion = RustcVersion::new($major, $minor, $patch); + )*)* + }; +} + +// names may refer to stabilized feature flags or library items +msrv_aliases! { + 1,53,0 { OR_PATTERNS } + 1,50,0 { BOOL_THEN } + 1,46,0 { CONST_IF_MATCH } + 1,45,0 { STR_STRIP_PREFIX } + 1,42,0 { MATCHES_MACRO } + 1,41,0 { RE_REBALANCING_COHERENCE, RESULT_MAP_OR_ELSE } + 1,40,0 { MEM_TAKE, NON_EXHAUSTIVE, OPTION_AS_DEREF } + 1,38,0 { POINTER_CAST } + 1,37,0 { TYPE_ALIAS_ENUM_VARIANTS } + 1,36,0 { ITERATOR_COPIED } + 1,35,0 { OPTION_COPIED, RANGE_CONTAINS } + 1,34,0 { TRY_FROM } + 1,30,0 { ITERATOR_FIND_MAP } + 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST } +} diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index b2ce58b597b3d..a08dcf19e5b51 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -364,7 +364,7 @@ fn check_terminator( fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<&RustcVersion>) -> bool { rustc_mir::const_eval::is_const_fn(tcx, def_id) - && if let Some(const_stab) = tcx.lookup_const_stability(def_id) { + && tcx.lookup_const_stability(def_id).map_or(true, |const_stab| { if let rustc_attr::StabilityLevel::Stable { since } = const_stab.level { // Checking MSRV is manually necessary because `rustc` has no such concept. This entire // function could be removed if `rustc` provided a MSRV-aware version of `is_const_fn`. @@ -375,10 +375,8 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<&RustcVersion>) -> b .expect("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted"), ) } else { - // `rustc_mir::const_eval::is_const_fn` should return false for unstably const functions. - unreachable!(); + // Unstable const fn with the feature enabled. + msrv.is_none() } - } else { - true - } + }) } diff --git a/doc/adding_lints.md b/doc/adding_lints.md index 50f0d724016ff..d6cc6d0c2c763 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -390,17 +390,23 @@ pass. ## Specifying the lint's minimum supported Rust version (MSRV) -Projects supporting older versions of Rust would need to disable a lint if it -targets features present in later versions. Support for this can be added by -specifying an MSRV in your lint like so, +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 -const MANUAL_STRIP_MSRV: RustcVersion = RustcVersion::new(1, 45, 0); +msrv_aliases! { + .. + 1,45,0 { STR_STRIP_PREFIX } +} ``` -The project's MSRV will also have to be an attribute in the lint so you'll have -to add a struct and constructor for your lint. The project's MSRV needs to be -passed when the lint is registered in `lib.rs` +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 { @@ -415,11 +421,11 @@ impl ManualStrip { } ``` -The project's MSRV can then be matched against the lint's `msrv` in the LintPass +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(), &MANUAL_STRIP_MSRV) { +if !meets_msrv(self.msrv.as_ref(), &msrvs::STR_STRIP_PREFIX) { return; } ``` diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index bfb0c3b3f74ec..f6a75595c986b 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -22,9 +22,16 @@ use rayon::prelude::*; use serde::{Deserialize, Serialize}; use serde_json::Value; +#[cfg(not(windows))] const CLIPPY_DRIVER_PATH: &str = "target/debug/clippy-driver"; +#[cfg(not(windows))] const CARGO_CLIPPY_PATH: &str = "target/debug/cargo-clippy"; +#[cfg(windows)] +const CLIPPY_DRIVER_PATH: &str = "target/debug/clippy-driver.exe"; +#[cfg(windows)] +const CARGO_CLIPPY_PATH: &str = "target/debug/cargo-clippy.exe"; + const LINTCHECK_DOWNLOADS: &str = "target/lintcheck/downloads"; const LINTCHECK_SOURCES: &str = "target/lintcheck/sources"; diff --git a/tests/ui/crashes/ice-7126.rs b/tests/ui/crashes/ice-7126.rs new file mode 100644 index 0000000000000..ca563ba097851 --- /dev/null +++ b/tests/ui/crashes/ice-7126.rs @@ -0,0 +1,14 @@ +// This test requires a feature gated const fn and will stop working in the future. + +#![feature(const_btree_new)] + +use std::collections::BTreeMap; + +struct Foo(BTreeMap); +impl Foo { + fn new() -> Self { + Self(BTreeMap::new()) + } +} + +fn main() {} diff --git a/tests/ui/iter_cloned_collect.fixed b/tests/ui/iter_cloned_collect.fixed index 2773227e26bca..39cc58cd29843 100644 --- a/tests/ui/iter_cloned_collect.fixed +++ b/tests/ui/iter_cloned_collect.fixed @@ -19,4 +19,8 @@ fn main() { let _: Vec = std::ffi::CStr::from_ptr(std::ptr::null()) .to_bytes().to_vec(); } + + // Issue #6808 + let arr: [u8; 64] = [0; 64]; + let _: Vec<_> = arr.to_vec(); } diff --git a/tests/ui/iter_cloned_collect.rs b/tests/ui/iter_cloned_collect.rs index 60a4eac23c79f..c2a036ec09f1e 100644 --- a/tests/ui/iter_cloned_collect.rs +++ b/tests/ui/iter_cloned_collect.rs @@ -22,4 +22,8 @@ fn main() { .cloned() .collect(); } + + // Issue #6808 + let arr: [u8; 64] = [0; 64]; + let _: Vec<_> = arr.iter().cloned().collect(); } diff --git a/tests/ui/iter_cloned_collect.stderr b/tests/ui/iter_cloned_collect.stderr index b90a1e6c91967..e1df61794cece 100644 --- a/tests/ui/iter_cloned_collect.stderr +++ b/tests/ui/iter_cloned_collect.stderr @@ -22,5 +22,11 @@ LL | | .cloned() LL | | .collect(); | |______________________^ help: try: `.to_vec()` -error: aborting due to 3 previous errors +error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable + --> $DIR/iter_cloned_collect.rs:28:24 + | +LL | let _: Vec<_> = arr.iter().cloned().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()` + +error: aborting due to 4 previous errors diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index f1d3252230bc2..e7a29596b73ac 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -151,4 +151,16 @@ const fn const_fn_result_unwrap_or() { }; } +mod issue6965 { + macro_rules! some_macro { + () => { + if 1 > 2 { Some(1) } else { None } + }; + } + + fn test() { + let _ = some_macro!().unwrap_or(0); + } +} + fn main() {} diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index c9eee25a5b153..66006b6c616f0 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -190,4 +190,19 @@ const fn const_fn_result_unwrap_or() { }; } +mod issue6965 { + macro_rules! some_macro { + () => { + if 1 > 2 { Some(1) } else { None } + }; + } + + fn test() { + let _ = match some_macro!() { + Some(val) => val, + None => 0, + }; + } +} + fn main() {} diff --git a/tests/ui/manual_unwrap_or.stderr b/tests/ui/manual_unwrap_or.stderr index fc174c4c2705d..99625b789b6a4 100644 --- a/tests/ui/manual_unwrap_or.stderr +++ b/tests/ui/manual_unwrap_or.stderr @@ -141,5 +141,15 @@ LL | | Err(_) => "Alice", LL | | }; | |_____^ help: replace with: `Ok::<&str, &str>("Bob").unwrap_or("Alice")` -error: aborting due to 13 previous errors +error: this pattern reimplements `Option::unwrap_or` + --> $DIR/manual_unwrap_or.rs:201:17 + | +LL | let _ = match some_macro!() { + | _________________^ +LL | | Some(val) => val, +LL | | None => 0, +LL | | }; + | |_________^ help: replace with: `some_macro!().unwrap_or(0)` + +error: aborting due to 14 previous errors diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index 49ace1ca128c1..7f9f7ddc53575 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -4,6 +4,10 @@ use std::ops::{Deref, RangeFrom}; +fn cloned_instead_of_copied() { + let _ = [1].iter().cloned(); +} + fn option_as_ref_deref() { let mut opt = Some(String::from("123")); diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr index 8d3575c2da83b..ddb1e1f372409 100644 --- a/tests/ui/min_rust_version_attr.stderr +++ b/tests/ui/min_rust_version_attr.stderr @@ -1,12 +1,12 @@ error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:156:24 + --> $DIR/min_rust_version_attr.rs:160:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::manual-strip` implied by `-D warnings` note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:155:9 + --> $DIR/min_rust_version_attr.rs:159:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,13 +17,13 @@ LL | assert_eq!(.to_uppercase(), "WORLD!"); | error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:168:24 + --> $DIR/min_rust_version_attr.rs:172:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:167:9 + --> $DIR/min_rust_version_attr.rs:171:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/needless_question_mark.fixed b/tests/ui/needless_question_mark.fixed index fd8433870bb1b..52ddd9d2dc826 100644 --- a/tests/ui/needless_question_mark.fixed +++ b/tests/ui/needless_question_mark.fixed @@ -96,78 +96,6 @@ where fn main() {} -mod question_mark_none { - #![clippy::msrv = "1.12.0"] - fn needless_question_mark_option() -> Option { - struct TO { - magic: Option, - } - let to = TO { magic: None }; - Some(to.magic?) // should not be triggered - } - - fn needless_question_mark_result() -> Result { - struct TO { - magic: Result, - } - let to = TO { magic: Ok(1_usize) }; - Ok(to.magic?) // should not be triggered - } - - fn main() { - needless_question_mark_option(); - needless_question_mark_result(); - } -} - -mod question_mark_result { - #![clippy::msrv = "1.21.0"] - fn needless_question_mark_option() -> Option { - struct TO { - magic: Option, - } - let to = TO { magic: None }; - Some(to.magic?) // should not be triggered - } - - fn needless_question_mark_result() -> Result { - struct TO { - magic: Result, - } - let to = TO { magic: Ok(1_usize) }; - to.magic // should be triggered - } - - fn main() { - needless_question_mark_option(); - needless_question_mark_result(); - } -} - -mod question_mark_both { - #![clippy::msrv = "1.22.0"] - fn needless_question_mark_option() -> Option { - struct TO { - magic: Option, - } - let to = TO { magic: None }; - to.magic // should be triggered - } - - fn needless_question_mark_result() -> Result { - struct TO { - magic: Result, - } - let to = TO { magic: Ok(1_usize) }; - to.magic // should be triggered - } - - fn main() { - needless_question_mark_option(); - needless_question_mark_result(); - } -} - // #6921 if a macro wraps an expr in Some( ) and the ? is in the macro use, // the suggestion fails to apply; do not lint macro_rules! some_in_macro { diff --git a/tests/ui/needless_question_mark.rs b/tests/ui/needless_question_mark.rs index 36d45ac7e03e2..1ea4ba0d83fd7 100644 --- a/tests/ui/needless_question_mark.rs +++ b/tests/ui/needless_question_mark.rs @@ -96,78 +96,6 @@ where fn main() {} -mod question_mark_none { - #![clippy::msrv = "1.12.0"] - fn needless_question_mark_option() -> Option { - struct TO { - magic: Option, - } - let to = TO { magic: None }; - Some(to.magic?) // should not be triggered - } - - fn needless_question_mark_result() -> Result { - struct TO { - magic: Result, - } - let to = TO { magic: Ok(1_usize) }; - Ok(to.magic?) // should not be triggered - } - - fn main() { - needless_question_mark_option(); - needless_question_mark_result(); - } -} - -mod question_mark_result { - #![clippy::msrv = "1.21.0"] - fn needless_question_mark_option() -> Option { - struct TO { - magic: Option, - } - let to = TO { magic: None }; - Some(to.magic?) // should not be triggered - } - - fn needless_question_mark_result() -> Result { - struct TO { - magic: Result, - } - let to = TO { magic: Ok(1_usize) }; - Ok(to.magic?) // should be triggered - } - - fn main() { - needless_question_mark_option(); - needless_question_mark_result(); - } -} - -mod question_mark_both { - #![clippy::msrv = "1.22.0"] - fn needless_question_mark_option() -> Option { - struct TO { - magic: Option, - } - let to = TO { magic: None }; - Some(to.magic?) // should be triggered - } - - fn needless_question_mark_result() -> Result { - struct TO { - magic: Result, - } - let to = TO { magic: Ok(1_usize) }; - Ok(to.magic?) // should be triggered - } - - fn main() { - needless_question_mark_option(); - needless_question_mark_result(); - } -} - // #6921 if a macro wraps an expr in Some( ) and the ? is in the macro use, // the suggestion fails to apply; do not lint macro_rules! some_in_macro { diff --git a/tests/ui/needless_question_mark.stderr b/tests/ui/needless_question_mark.stderr index 7cbf1e505adf6..afd68d91e51fe 100644 --- a/tests/ui/needless_question_mark.stderr +++ b/tests/ui/needless_question_mark.stderr @@ -67,25 +67,7 @@ LL | return Ok(t.magic?); | ^^^^^^^^^^^^ help: try: `t.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:138:9 - | -LL | Ok(to.magic?) // should be triggered - | ^^^^^^^^^^^^^ help: try: `to.magic` - -error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:154:9 - | -LL | Some(to.magic?) // should be triggered - | ^^^^^^^^^^^^^^^ help: try: `to.magic` - -error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:162:9 - | -LL | Ok(to.magic?) // should be triggered - | ^^^^^^^^^^^^^ help: try: `to.magic` - -error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:187:27 + --> $DIR/needless_question_mark.rs:115:27 | LL | || -> Option<_> { Some(Some($expr)?) }() | ^^^^^^^^^^^^^^^^^^ help: try: `Some($expr)` @@ -95,5 +77,5 @@ LL | let _x = some_and_qmark_in_macro!(x?); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 15 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/single_component_path_imports_macro.fixed b/tests/ui/single_component_path_imports_macro.fixed new file mode 100644 index 0000000000000..05863f9a2bf48 --- /dev/null +++ b/tests/ui/single_component_path_imports_macro.fixed @@ -0,0 +1,21 @@ +// run-rustfix +// edition:2018 +#![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!(); +} diff --git a/tests/ui/single_component_path_imports_macro.rs b/tests/ui/single_component_path_imports_macro.rs new file mode 100644 index 0000000000000..633deea348b81 --- /dev/null +++ b/tests/ui/single_component_path_imports_macro.rs @@ -0,0 +1,21 @@ +// run-rustfix +// edition:2018 +#![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!(); +} diff --git a/tests/ui/single_component_path_imports_macro.stderr b/tests/ui/single_component_path_imports_macro.stderr new file mode 100644 index 0000000000000..239efb393b1ab --- /dev/null +++ b/tests/ui/single_component_path_imports_macro.stderr @@ -0,0 +1,10 @@ +error: this import is redundant + --> $DIR/single_component_path_imports_macro.rs:16: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 + diff --git a/tests/ui/single_element_loop.fixed b/tests/ui/single_element_loop.fixed index 8ca068293a611..c307afffcb863 100644 --- a/tests/ui/single_element_loop.fixed +++ b/tests/ui/single_element_loop.fixed @@ -8,4 +8,9 @@ fn main() { let item = &item1; println!("{}", item); } + + { + let item = &item1; + println!("{:?}", item); + } } diff --git a/tests/ui/single_element_loop.rs b/tests/ui/single_element_loop.rs index 57e9336a31fcf..2c0c03b721199 100644 --- a/tests/ui/single_element_loop.rs +++ b/tests/ui/single_element_loop.rs @@ -7,4 +7,8 @@ fn main() { for item in &[item1] { println!("{}", item); } + + for item in [item1].iter() { + println!("{:?}", item); + } } diff --git a/tests/ui/single_element_loop.stderr b/tests/ui/single_element_loop.stderr index 90be1dc328371..0e35a33ded5ba 100644 --- a/tests/ui/single_element_loop.stderr +++ b/tests/ui/single_element_loop.stderr @@ -15,5 +15,21 @@ LL | println!("{}", item); LL | } | -error: aborting due to previous error +error: for loop over a single element + --> $DIR/single_element_loop.rs:11:5 + | +LL | / for item in [item1].iter() { +LL | | println!("{:?}", item); +LL | | } + | |_____^ + | +help: try + | +LL | { +LL | let item = &item1; +LL | println!("{:?}", item); +LL | } + | + +error: aborting due to 2 previous errors diff --git a/tests/ui/unused_io_amount.rs b/tests/ui/unused_io_amount.rs index ebaba9629db16..8b141e25942d7 100644 --- a/tests/ui/unused_io_amount.rs +++ b/tests/ui/unused_io_amount.rs @@ -1,7 +1,7 @@ #![allow(dead_code)] #![warn(clippy::unused_io_amount)] -use std::io; +use std::io::{self, Read}; fn question_mark(s: &mut T) -> io::Result<()> { s.write(b"test")?; @@ -22,4 +22,43 @@ fn vectored(s: &mut T) -> io::Result<()> { Ok(()) } +fn ok(file: &str) -> Option<()> { + let mut reader = std::fs::File::open(file).ok()?; + let mut result = [0u8; 0]; + reader.read(&mut result).ok()?; + Some(()) +} + +#[allow(clippy::redundant_closure)] +#[allow(clippy::bind_instead_of_map)] +fn or_else(file: &str) -> io::Result<()> { + let mut reader = std::fs::File::open(file)?; + let mut result = [0u8; 0]; + reader.read(&mut result).or_else(|err| Err(err))?; + Ok(()) +} + +#[derive(Debug)] +enum Error { + Kind, +} + +fn or(file: &str) -> Result<(), Error> { + let mut reader = std::fs::File::open(file).unwrap(); + let mut result = [0u8; 0]; + reader.read(&mut result).or(Err(Error::Kind))?; + Ok(()) +} + +fn combine_or(file: &str) -> Result<(), Error> { + let mut reader = std::fs::File::open(file).unwrap(); + let mut result = [0u8; 0]; + reader + .read(&mut result) + .or(Err(Error::Kind)) + .or(Err(Error::Kind)) + .expect("error"); + Ok(()) +} + fn main() {} diff --git a/tests/ui/unused_io_amount.stderr b/tests/ui/unused_io_amount.stderr index 5219d63980b4b..d8dfc0e5a798c 100644 --- a/tests/ui/unused_io_amount.stderr +++ b/tests/ui/unused_io_amount.stderr @@ -36,5 +36,33 @@ error: written amount is not handled LL | s.write_vectored(&[io::IoSlice::new(&[])])?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 6 previous errors +error: read amount is not handled. Use `Read::read_exact` instead + --> $DIR/unused_io_amount.rs:28:5 + | +LL | reader.read(&mut result).ok()?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: read amount is not handled. Use `Read::read_exact` instead + --> $DIR/unused_io_amount.rs:37:5 + | +LL | reader.read(&mut result).or_else(|err| Err(err))?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: read amount is not handled. Use `Read::read_exact` instead + --> $DIR/unused_io_amount.rs:49:5 + | +LL | reader.read(&mut result).or(Err(Error::Kind))?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: read amount is not handled. Use `Read::read_exact` instead + --> $DIR/unused_io_amount.rs:56:5 + | +LL | / reader +LL | | .read(&mut result) +LL | | .or(Err(Error::Kind)) +LL | | .or(Err(Error::Kind)) +LL | | .expect("error"); + | |________________________^ + +error: aborting due to 10 previous errors From af0dde24b96b739d66c41a2a67077eae377a86aa Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Wed, 14 Apr 2021 09:20:49 -0400 Subject: [PATCH 09/67] Implement `x.py test src/tools/clippy --bless` - Add clippy_dev to the rust workspace Before, it would give an error that it wasn't either included or excluded from the workspace: ``` error: current package believes it's in a workspace when it's not: current: /home/joshua/rustc/src/tools/clippy/clippy_dev/Cargo.toml workspace: /home/joshua/rustc/Cargo.toml this may be fixable by adding `src/tools/clippy/clippy_dev` to the `workspace.members` array of the manifest located at: /home/joshua/rustc/Cargo.toml Alternatively, to keep it out of the workspace, add the package to the `workspace.exclude` array, or add an empty `[workspace]` table to the package's manifest. ``` - Change clippy's copy of compiletest not to special-case rust-lang/rust. Using OUT_DIR confused `clippy_dev` and it couldn't find the test outputs. This is one of the reasons why `cargo dev bless` used to silently do nothing (the others were that `CARGO_TARGET_DIR` and `PROFILE` weren't set appropriately). - Run clippy_dev on test failure I tested this by removing a couple lines from a stderr file, and they were correctly replaced. - Fix clippy_dev warnings --- clippy_dev/src/new_lint.rs | 4 ++-- tests/compile-test.rs | 9 +-------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index d951ca0e6308d..4676c2affad79 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -44,7 +44,7 @@ pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str create_test(&lint).context("Unable to create a test for the new lint") } -fn create_lint(lint: &LintData) -> io::Result<()> { +fn create_lint(lint: &LintData<'_>) -> io::Result<()> { 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"), @@ -68,7 +68,7 @@ fn create_lint(lint: &LintData) -> io::Result<()> { write_file(lint.project_root.join(&lint_path), lint_contents.as_bytes()) } -fn create_test(lint: &LintData) -> io::Result<()> { +fn create_test(lint: &LintData<'_>) -> io::Result<()> { fn create_project_layout>(lint_name: &str, location: P, case: &str, hint: &str) -> io::Result<()> { let mut path = location.into().join(case); fs::create_dir(&path)?; diff --git a/tests/compile-test.rs b/tests/compile-test.rs index f4d354f0bf942..e1110721f6ece 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -83,14 +83,7 @@ fn default_config() -> compiletest::Config { third_party_crates(), )); - config.build_base = if cargo::is_rustc_test_suite() { - // This make the stderr files go to clippy OUT_DIR on rustc repo build dir - let mut path = PathBuf::from(env!("OUT_DIR")); - path.push("test_build_base"); - path - } else { - host_lib().join("test_build_base") - }; + config.build_base = host_lib().join("test_build_base"); config.rustc_path = clippy_driver_path(); config } From f4bd12cfde4028d793c44f2cf42ad00fe295e8c6 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Tue, 27 Apr 2021 13:00:36 -0400 Subject: [PATCH 10/67] Switch `rustc::internal` from deny to warn These should still obey deny-warnings. --- clippy_lints/src/lib.rs | 2 +- src/driver.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 11fef30945d78..22e6bb822b8c2 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -17,7 +17,7 @@ // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] // warn on rustc internal lints -#![deny(rustc::internal)] +#![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`.) diff --git a/src/driver.rs b/src/driver.rs index fa0c5f01430b6..750a23e8c9841 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -4,7 +4,7 @@ // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] // warn on rustc internal lints -#![deny(rustc::internal)] +#![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`.) From 572c405da063d2628378b74a8f659ce90e0e7779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Tue, 27 Apr 2021 23:16:19 +0200 Subject: [PATCH 11/67] fix ice when checking rustc libstd thread 'rustc' panicked at 'index out of bounds: the len is 0 but the index is 0', src/tools/clippy/clippy_lints/src/matches.rs:1595:53 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace I don't have a minimised testcase because I don't have time to reduce libstd down to a few lines right now. --- clippy_lints/src/matches.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 13b2a834b0a96..c7a25095bf6fa 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1590,9 +1590,9 @@ fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { // 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 { if_chain! { - if let PatKind::TupleStruct(ref qpath, pats, _) = arm.pat.kind; + if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind; if is_lang_ctor(cx, qpath, OptionSome); - if let PatKind::Binding(rb, .., ident, _) = pats[0].kind; + 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; From 32351d6b9f7caa2fd197a23a190fb32392ab9dfd Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 28 Apr 2021 14:29:13 -0500 Subject: [PATCH 12/67] Remove leftover plugin conf_file code --- clippy_lints/src/lib.rs | 75 ++++++++++++++-------------------- clippy_lints/src/utils/conf.rs | 23 ----------- src/driver.rs | 2 +- 3 files changed, 31 insertions(+), 69 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c84890299df09..57843afaddbc6 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -399,56 +399,41 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore) { } #[doc(hidden)] -pub fn read_conf(args: &[rustc_ast::NestedMetaItem], sess: &Session) -> Conf { +pub fn read_conf(sess: &Session) -> Conf { use std::path::Path; - match utils::conf::file_from_args(args) { - Ok(file_name) => { - // if the user specified a file, it must exist, otherwise default to `clippy.toml` but - // do not require the file to exist - let file_name = match file_name { - Some(file_name) => file_name, - None => match utils::conf::lookup_conf_file() { - Ok(Some(path)) => path, - Ok(None) => return Conf::default(), - Err(error) => { - sess.struct_err(&format!("error finding Clippy's configuration file: {}", error)) - .emit(); - return Conf::default(); - }, - }, - }; - - let file_name = if file_name.is_relative() { - sess.local_crate_source_file - .as_deref() - .and_then(Path::parent) - .unwrap_or_else(|| Path::new("")) - .join(file_name) - } else { - file_name - }; + let 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 (conf, errors) = utils::conf::read(&file_name); + let file_name = if file_name.is_relative() { + sess.local_crate_source_file + .as_deref() + .and_then(Path::parent) + .unwrap_or_else(|| Path::new("")) + .join(file_name) + } else { + file_name + }; - // 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(); - } + let (conf, errors) = utils::conf::read(&file_name); - conf - }, - Err((err, span)) => { - sess.struct_span_err(span, err) - .span_note(span, "Clippy will use default configuration") - .emit(); - Conf::default() - }, + // 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 diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index d56855a71c159..e221be6353810 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -2,34 +2,11 @@ #![deny(clippy::missing_docs_in_private_items)] -use rustc_ast::ast::{LitKind, MetaItemKind, NestedMetaItem}; -use rustc_span::source_map; -use source_map::Span; use std::lazy::SyncLazy; use std::path::{Path, PathBuf}; use std::sync::Mutex; use std::{env, fmt, fs, io}; -/// Gets the configuration file from arguments. -pub fn file_from_args(args: &[NestedMetaItem]) -> Result, (&'static str, Span)> { - for arg in args.iter().filter_map(NestedMetaItem::meta_item) { - if arg.has_name(sym!(conf_file)) { - return match arg.kind { - MetaItemKind::Word | MetaItemKind::List(_) => Err(("`conf_file` must be a named value", arg.span)), - MetaItemKind::NameValue(ref value) => { - if let LitKind::Str(ref file, _) = value.kind { - Ok(Some(file.to_string().into())) - } else { - Err(("`conf_file` value must be a string", value.span)) - } - }, - }; - } - } - - Ok(None) -} - /// Error from reading a configuration file. #[derive(Debug)] pub enum Error { diff --git a/src/driver.rs b/src/driver.rs index fa0c5f01430b6..221474e55d485 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -106,7 +106,7 @@ impl rustc_driver::Callbacks for ClippyCallbacks { (previous)(sess, lint_store); } - let conf = clippy_lints::read_conf(&[], sess); + let conf = clippy_lints::read_conf(sess); clippy_lints::register_plugins(lint_store, sess, &conf); clippy_lints::register_pre_expansion_lints(lint_store); clippy_lints::register_renamed(lint_store); From 2f494557baca7101ce0a540e5270fa25026873bb Mon Sep 17 00:00:00 2001 From: Charles Lew Date: Mon, 26 Apr 2021 01:09:35 +0800 Subject: [PATCH 13/67] Implement RFC 1260 with feature_name `imported_main`. --- clippy_utils/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index cd85c487798d9..e81a92eb74ca7 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -678,7 +678,7 @@ pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option, def_id: DefId) -> bool { cx.tcx .entry_fn(LOCAL_CRATE) - .map_or(false, |(entry_fn_def_id, _)| def_id == entry_fn_def_id.to_def_id()) + .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]`. From 63425de77db7f4dee55cfceeed7e174bfe986b0d Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Thu, 29 Apr 2021 10:10:58 +0200 Subject: [PATCH 14/67] while_immutable_cond: check condition for mutation --- clippy_lints/src/loops/while_immutable_condition.rs | 13 ++++++++----- tests/ui/infinite_loop.rs | 12 ++++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/loops/while_immutable_condition.rs b/clippy_lints/src/loops/while_immutable_condition.rs index de267cc77d2b4..55404b87ec9ce 100644 --- a/clippy_lints/src/loops/while_immutable_condition.rs +++ b/clippy_lints/src/loops/while_immutable_condition.rs @@ -28,11 +28,14 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, expr: &' return; } let used_in_condition = &var_visitor.ids; - let no_cond_variable_mutated = if let Some(used_mutably) = mutated_variables(expr, cx) { - used_in_condition.is_disjoint(&used_mutably) - } else { - return; - }; + let mutated_in_body = mutated_variables(expr, cx); + let mutated_in_condition = mutated_variables(cond, cx); + let no_cond_variable_mutated = + if let (Some(used_mutably_body), Some(used_mutably_cond)) = (mutated_in_body, mutated_in_condition) { + used_in_condition.is_disjoint(&used_mutably_body) && used_in_condition.is_disjoint(&used_mutably_cond) + } else { + return; + }; let mutable_static_in_cond = var_visitor.def_ids.iter().any(|(_, v)| *v); let mut has_break_or_return_visitor = HasBreakOrReturnVisitor { diff --git a/tests/ui/infinite_loop.rs b/tests/ui/infinite_loop.rs index 72591f12baf85..3d8fb8507e515 100644 --- a/tests/ui/infinite_loop.rs +++ b/tests/ui/infinite_loop.rs @@ -192,11 +192,23 @@ fn while_loop_with_break_and_return() { } } +fn immutable_condition_false_positive(mut n: u64) -> u32 { + let mut count = 0; + while { + n >>= 1; + n != 0 + } { + count += 1; + } + count +} + fn main() { immutable_condition(); unused_var(); used_immutable(); internally_mutable(); + immutable_condition_false_positive(5); let mut c = Counter { count: 0 }; c.inc_n(5); From 6ddd3c9e36c47288ebd4eccc72b2c98d36efe792 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Thu, 29 Apr 2021 18:37:22 +0200 Subject: [PATCH 15/67] Fix clippy error --- tests/ui/crashes/ice-3969.stderr | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/ui/crashes/ice-3969.stderr b/tests/ui/crashes/ice-3969.stderr index 923db0664a714..fb4589a48ec42 100644 --- a/tests/ui/crashes/ice-3969.stderr +++ b/tests/ui/crashes/ice-3969.stderr @@ -5,18 +5,26 @@ LL | for<'a> Dst: Sized, | ^^^^^^ help: use `dyn`: `dyn A + 'a` | = note: `-D bare-trait-objects` implied by `-D warnings` + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! + = note: for more information, see issue #80165 error: trait objects without an explicit `dyn` are deprecated --> $DIR/ice-3969.rs:27:16 | LL | let x: Dst = *(Box::new(Dst { x: 1 }) as Box>); | ^ help: use `dyn`: `dyn A` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! + = note: for more information, see issue #80165 error: trait objects without an explicit `dyn` are deprecated --> $DIR/ice-3969.rs:27:57 | LL | let x: Dst = *(Box::new(Dst { x: 1 }) as Box>); | ^ help: use `dyn`: `dyn A` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! + = note: for more information, see issue #80165 error: aborting due to 3 previous errors From 0dff377a62b3ae24ceb88ff0a9070f3a4e3d5552 Mon Sep 17 00:00:00 2001 From: Takayuki Date: Fri, 30 Apr 2021 14:40:35 +0900 Subject: [PATCH 16/67] use `in_constant` --- clippy_lints/src/comparison_chain.rs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index 150d8a3d6739e..2a61d58e6537d 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::implements_trait; -use clippy_utils::{get_trait_def_id, if_sequence, is_else_clause, paths, SpanlessEq}; -use rustc_hir::{BinOpKind, Expr, ExprKind, Node}; +use clippy_utils::{get_trait_def_id, if_sequence, in_constant, is_else_clause, paths, SpanlessEq}; +use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { return; } - if parent_node_is_if_const_fn(cx, expr) { + if in_constant(cx, expr.hir_id) { return; } @@ -127,11 +127,3 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { fn kind_is_cmp(kind: BinOpKind) -> bool { matches!(kind, BinOpKind::Lt | BinOpKind::Gt | BinOpKind::Eq) } - -fn parent_node_is_if_const_fn(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - match cx.tcx.hir().find(cx.tcx.hir().get_parent_item(expr.hir_id)) { - Some(Node::Item(item)) => rustc_mir::const_eval::is_const_fn(cx.tcx, item.def_id.to_def_id()), - Some(Node::ImplItem(impl_item)) => rustc_mir::const_eval::is_const_fn(cx.tcx, impl_item.def_id.to_def_id()), - _ => false, - } -} From a7dc1ca80a298fbc8d948e98490c4cd7661156e5 Mon Sep 17 00:00:00 2001 From: Mateusz Gacek Date: Fri, 30 Apr 2021 17:27:42 +0200 Subject: [PATCH 17/67] Small fix in CONTRIBUTING.md --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 02d2b63c9e199..7265d1b832376 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -118,7 +118,7 @@ which `IntelliJ Rust` will be able to understand. Run `cargo dev ide_setup --repo-path ` where `` 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 rust-analyzer to understand most of the types that Clippy uses. +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/ From b9c8e683d609d204fd1192e236a92099d94cbef2 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 27 Apr 2021 21:03:48 -0500 Subject: [PATCH 18/67] Disable default_trait_access in macros --- clippy_lints/src/default.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index 710da8fe9e037..7a53d390bb45f 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg}; use clippy_utils::source::snippet_with_macro_callsite; -use clippy_utils::{any_parent_is_automatically_derived, contains_name, match_def_path, paths}; +use clippy_utils::{any_parent_is_automatically_derived, contains_name, in_macro, match_def_path, paths}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -75,6 +75,7 @@ impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]); impl LateLintPass<'_> for Default { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { + if !in_macro(expr.span); // Avoid cases already linted by `field_reassign_with_default` if !self.reassigned_linted.contains(&expr.span); if let ExprKind::Call(path, ..) = expr.kind; From 1e22e564e4d272c296a6acb8596831fbaa05bc7b Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 27 Apr 2021 21:04:06 -0500 Subject: [PATCH 19/67] Refactor config deserialization --- clippy_lints/src/lib.rs | 4 +- clippy_lints/src/utils/conf.rs | 232 ++++++++---------- .../bad_toml_type/conf_bad_type.stderr | 2 +- .../conf_deprecated_key.stderr | 2 +- 4 files changed, 108 insertions(+), 132 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 57843afaddbc6..40a793e48cf0d 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -382,6 +382,7 @@ 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 /// @@ -421,8 +422,7 @@ pub fn read_conf(sess: &Session) -> Conf { file_name }; - let (conf, errors) = utils::conf::read(&file_name); - + 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!( diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index e221be6353810..747d92dd74238 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -1,98 +1,111 @@ //! Read configurations files. -#![deny(clippy::missing_docs_in_private_items)] +#![allow(clippy::module_name_repetitions)] -use std::lazy::SyncLazy; +use serde::de::{Deserializer, IgnoredAny, IntoDeserializer, MapAccess, Visitor}; +use serde::Deserialize; +use std::error::Error; use std::path::{Path, PathBuf}; -use std::sync::Mutex; use std::{env, fmt, fs, io}; -/// Error from reading a configuration file. -#[derive(Debug)] -pub enum Error { - /// An I/O error. - Io(io::Error), - /// Not valid toml or doesn't fit the expected config format - Toml(String), +/// Conf with parse errors +#[derive(Default)] +pub struct TryConf { + pub conf: Conf, + pub errors: Vec, } -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Io(err) => err.fmt(f), - Self::Toml(err) => err.fmt(f), +impl TryConf { + fn from_error(error: impl Error) -> Self { + Self { + conf: Conf::default(), + errors: vec![error.to_string()], } } } -impl From for Error { - fn from(e: io::Error) -> Self { - Self::Io(e) - } -} +macro_rules! define_Conf { + ($( + #[$doc:meta] + $(#[conf_deprecated($dep:literal)])? + ($name:ident: $ty:ty $(= $default:expr)?), + )*) => { + /// Clippy lint configuration + pub struct Conf { + $(#[$doc] pub $name: $ty,)* + } -/// Vec of errors that might be collected during config toml parsing -static ERRORS: SyncLazy>> = SyncLazy::new(|| Mutex::new(Vec::new())); + mod defaults { + $(pub fn $name() -> $ty { define_Conf!(@default $($default)?) })* + } -macro_rules! define_Conf { - ($(#[$doc:meta] ($config:ident, $config_str:literal: $Ty:ty, $default:expr),)+) => { - mod helpers { - use serde::Deserialize; - /// Type used to store lint configuration. - #[derive(Deserialize)] - #[serde(rename_all = "kebab-case", deny_unknown_fields)] - pub struct Conf { - $( - #[$doc] - #[serde(default = $config_str)] - #[serde(with = $config_str)] - pub $config: $Ty, - )+ - #[allow(dead_code)] - #[serde(default)] - third_party: Option<::toml::Value>, + impl Default for Conf { + fn default() -> Self { + Self { $($name: defaults::$name(),)* } } + } - $( - mod $config { - use serde::Deserialize; - pub fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<$Ty, D::Error> { - use super::super::{ERRORS, Error}; - - Ok( - <$Ty>::deserialize(deserializer).unwrap_or_else(|e| { - ERRORS - .lock() - .expect("no threading here") - .push(Error::Toml(e.to_string())); - super::$config() - }) - ) - } - } + impl<'de> Deserialize<'de> for TryConf { + fn deserialize(deserializer: D) -> Result 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") + } - #[must_use] - fn $config() -> $Ty { - let x = $default; - x + fn visit_map(self, mut map: V) -> Result 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::()) + } } - )+ + let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* }; + Ok(TryConf { conf, errors }) + } } }; + (@default) => (Default::default()); + (@default $default:expr) => ($default); } -pub use self::helpers::Conf; define_Conf! { /// Lint: 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. The minimum rust version that the project supports - (msrv, "msrv": Option, None), + (msrv: Option), /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses - (blacklisted_names, "blacklisted_names": Vec, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()), + (blacklisted_names: Vec = ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()), /// Lint: COGNITIVE_COMPLEXITY. The maximum cognitive complexity a function can have - (cognitive_complexity_threshold, "cognitive_complexity_threshold": u64, 25), + (cognitive_complexity_threshold: u64 = 25), /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. Use the Cognitive Complexity lint instead. - (cyclomatic_complexity_threshold, "cyclomatic_complexity_threshold": Option, None), + #[conf_deprecated("Please use `cognitive-complexity-threshold` instead.")] + (cyclomatic_complexity_threshold: Option), /// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks - (doc_valid_idents, "doc_valid_idents": Vec, [ + (doc_valid_idents: Vec = [ "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "ECMAScript", @@ -113,54 +126,47 @@ define_Conf! { "CamelCase", ].iter().map(ToString::to_string).collect()), /// Lint: TOO_MANY_ARGUMENTS. The maximum number of argument a function or method can have - (too_many_arguments_threshold, "too_many_arguments_threshold": u64, 7), + (too_many_arguments_threshold: u64 = 7), /// Lint: TYPE_COMPLEXITY. The maximum complexity a type can have - (type_complexity_threshold, "type_complexity_threshold": u64, 250), + (type_complexity_threshold: u64 = 250), /// Lint: MANY_SINGLE_CHAR_NAMES. The maximum number of single char bindings a scope may have - (single_char_binding_names_threshold, "single_char_binding_names_threshold": u64, 4), + (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, "too_large_for_stack": u64, 200), + (too_large_for_stack: u64 = 200), /// Lint: ENUM_VARIANT_NAMES. The minimum number of enum variants for the lints about variant names to trigger - (enum_variant_name_threshold, "enum_variant_name_threshold": u64, 3), + (enum_variant_name_threshold: u64 = 3), /// Lint: LARGE_ENUM_VARIANT. The maximum size of a enum's variant to avoid box suggestion - (enum_variant_size_threshold, "enum_variant_size_threshold": u64, 200), + (enum_variant_size_threshold: u64 = 200), /// Lint: VERBOSE_BIT_MASK. The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros' - (verbose_bit_mask_threshold, "verbose_bit_mask_threshold": u64, 1), + (verbose_bit_mask_threshold: u64 = 1), /// Lint: DECIMAL_LITERAL_REPRESENTATION. The lower bound for linting decimal literals - (literal_representation_threshold, "literal_representation_threshold": u64, 16384), + (literal_representation_threshold: u64 = 16384), /// Lint: TRIVIALLY_COPY_PASS_BY_REF. The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference. - (trivial_copy_size_limit, "trivial_copy_size_limit": Option, None), + (trivial_copy_size_limit: Option), /// 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, "pass_by_value_size_limit": u64, 256), + (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, "too_many_lines_threshold": u64, 100), + (too_many_lines_threshold: u64 = 100), /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. The maximum allowed size for arrays on the stack - (array_size_threshold, "array_size_threshold": u64, 512_000), + (array_size_threshold: u64 = 512_000), /// Lint: VEC_BOX. The size of the boxed type in bytes, where boxing in a `Vec` is allowed - (vec_box_size_threshold, "vec_box_size_threshold": u64, 4096), + (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, "max_trait_bounds": u64, 3), + (max_trait_bounds: u64 = 3), /// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bools a struct can have - (max_struct_bools, "max_struct_bools": u64, 3), + (max_struct_bools: u64 = 3), /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters can have - (max_fn_params_bools, "max_fn_params_bools": u64, 3), + (max_fn_params_bools: u64 = 3), /// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests). - (warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports": bool, false), + (warn_on_all_wildcard_imports: bool), /// Lint: DISALLOWED_METHOD. The list of disallowed methods, written as fully qualified paths. - (disallowed_methods, "disallowed_methods": Vec, Vec::::new()), + (disallowed_methods: Vec), /// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators. - (unreadable_literal_lint_fractions, "unreadable_literal_lint_fractions": bool, true), + (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, "upper_case_acronyms_aggressive": bool, false), + (upper_case_acronyms_aggressive: bool), /// Lint: _CARGO_COMMON_METADATA. For internal testing only, ignores the current `publish` settings in the Cargo manifest. - (cargo_ignore_publish, "cargo_ignore_publish": bool, false), -} - -impl Default for Conf { - #[must_use] - fn default() -> Self { - toml::from_str("").expect("we never error on empty config files") - } + (cargo_ignore_publish: bool), } /// Search for the configuration file. @@ -194,43 +200,13 @@ pub fn lookup_conf_file() -> io::Result> { } } -/// Produces a `Conf` filled with the default values and forwards the errors -/// -/// Used internally for convenience -fn default(errors: Vec) -> (Conf, Vec) { - (Conf::default(), errors) -} - /// Read the `toml` configuration file. /// /// In case of error, the function tries to continue as much as possible. -pub fn read(path: &Path) -> (Conf, Vec) { +pub fn read(path: &Path) -> TryConf { let content = match fs::read_to_string(path) { + Err(e) => return TryConf::from_error(e), Ok(content) => content, - Err(err) => return default(vec![err.into()]), }; - - assert!(ERRORS.lock().expect("no threading -> mutex always safe").is_empty()); - match toml::from_str(&content) { - Ok(toml) => { - let mut errors = ERRORS.lock().expect("no threading -> mutex always safe").split_off(0); - - let toml_ref: &Conf = &toml; - - let cyc_field: Option = toml_ref.cyclomatic_complexity_threshold; - - if cyc_field.is_some() { - let cyc_err = "found deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead.".to_string(); - errors.push(Error::Toml(cyc_err)); - } - - (toml, errors) - }, - Err(e) => { - let mut errors = ERRORS.lock().expect("no threading -> mutex always safe").split_off(0); - errors.push(Error::Toml(e.to_string())); - - default(errors) - }, - } + toml::from_str(&content).unwrap_or_else(TryConf::from_error) } diff --git a/tests/ui-toml/bad_toml_type/conf_bad_type.stderr b/tests/ui-toml/bad_toml_type/conf_bad_type.stderr index efd02bcbb6e28..c7bc261de6c5a 100644 --- a/tests/ui-toml/bad_toml_type/conf_bad_type.stderr +++ b/tests/ui-toml/bad_toml_type/conf_bad_type.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: invalid type: integer `42`, expected a sequence +error: error reading Clippy's configuration file `$DIR/clippy.toml`: invalid type: integer `42`, expected a sequence for key `blacklisted-names` error: aborting due to previous error diff --git a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr index 34267c0daf7c2..8bf9fe64c6d77 100644 --- a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr +++ b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: found deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead. +error: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead. error: aborting due to previous error From b75354034ca8a62623165a4e574ab2f604b9dee3 Mon Sep 17 00:00:00 2001 From: Kornel Date: Fri, 30 Apr 2021 12:15:38 +0100 Subject: [PATCH 20/67] Add ErrorKind::OutOfMemory --- tests/ui/wildcard_enum_match_arm.fixed | 3 ++- tests/ui/wildcard_enum_match_arm.rs | 1 + tests/ui/wildcard_enum_match_arm.stderr | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/ui/wildcard_enum_match_arm.fixed b/tests/ui/wildcard_enum_match_arm.fixed index 129d82652d759..5ad27bb14501f 100644 --- a/tests/ui/wildcard_enum_match_arm.fixed +++ b/tests/ui/wildcard_enum_match_arm.fixed @@ -77,7 +77,7 @@ fn main() { let error_kind = ErrorKind::NotFound; match error_kind { ErrorKind::NotFound => {}, - ErrorKind::PermissionDenied | ErrorKind::ConnectionRefused | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable | ErrorKind::BrokenPipe | ErrorKind::AlreadyExists | ErrorKind::WouldBlock | ErrorKind::InvalidInput | ErrorKind::InvalidData | ErrorKind::TimedOut | ErrorKind::WriteZero | ErrorKind::Interrupted | ErrorKind::Other | ErrorKind::UnexpectedEof | ErrorKind::Unsupported | _ => {}, + ErrorKind::PermissionDenied | ErrorKind::ConnectionRefused | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable | ErrorKind::BrokenPipe | ErrorKind::AlreadyExists | ErrorKind::WouldBlock | ErrorKind::InvalidInput | ErrorKind::InvalidData | ErrorKind::TimedOut | ErrorKind::WriteZero | ErrorKind::Interrupted | ErrorKind::Other | ErrorKind::UnexpectedEof | ErrorKind::Unsupported | ErrorKind::OutOfMemory | _ => {}, } match error_kind { ErrorKind::NotFound => {}, @@ -99,6 +99,7 @@ fn main() { ErrorKind::Other => {}, ErrorKind::UnexpectedEof => {}, ErrorKind::Unsupported => {}, + ErrorKind::OutOfMemory => {}, _ => {}, } } diff --git a/tests/ui/wildcard_enum_match_arm.rs b/tests/ui/wildcard_enum_match_arm.rs index 028ecb63e7e6d..adca0738bba5b 100644 --- a/tests/ui/wildcard_enum_match_arm.rs +++ b/tests/ui/wildcard_enum_match_arm.rs @@ -99,6 +99,7 @@ fn main() { ErrorKind::Other => {}, ErrorKind::UnexpectedEof => {}, ErrorKind::Unsupported => {}, + ErrorKind::OutOfMemory => {}, _ => {}, } } diff --git a/tests/ui/wildcard_enum_match_arm.stderr b/tests/ui/wildcard_enum_match_arm.stderr index fd45cad00d6b5..73f6a4a80c960 100644 --- a/tests/ui/wildcard_enum_match_arm.stderr +++ b/tests/ui/wildcard_enum_match_arm.stderr @@ -32,7 +32,7 @@ error: wildcard matches known variants and will also match future added variants --> $DIR/wildcard_enum_match_arm.rs:80:9 | LL | _ => {}, - | ^ help: try this: `ErrorKind::PermissionDenied | ErrorKind::ConnectionRefused | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable | ErrorKind::BrokenPipe | ErrorKind::AlreadyExists | ErrorKind::WouldBlock | ErrorKind::InvalidInput | ErrorKind::InvalidData | ErrorKind::TimedOut | ErrorKind::WriteZero | ErrorKind::Interrupted | ErrorKind::Other | ErrorKind::UnexpectedEof | ErrorKind::Unsupported | _` + | ^ help: try this: `ErrorKind::PermissionDenied | ErrorKind::ConnectionRefused | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable | ErrorKind::BrokenPipe | ErrorKind::AlreadyExists | ErrorKind::WouldBlock | ErrorKind::InvalidInput | ErrorKind::InvalidData | ErrorKind::TimedOut | ErrorKind::WriteZero | ErrorKind::Interrupted | ErrorKind::Other | ErrorKind::UnexpectedEof | ErrorKind::Unsupported | ErrorKind::OutOfMemory | _` error: aborting due to 5 previous errors From 857d9f15da507adbc3e59560f3c15568cd0ae0e5 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sun, 2 May 2021 16:56:46 -0500 Subject: [PATCH 21/67] Fix error punctuation --- clippy_lints/src/utils/conf.rs | 2 +- tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 747d92dd74238..aeca5b9cccee4 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -102,7 +102,7 @@ define_Conf! { /// 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.")] + #[conf_deprecated("Please use `cognitive-complexity-threshold` instead")] (cyclomatic_complexity_threshold: Option), /// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks (doc_valid_idents: Vec = [ diff --git a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr index 8bf9fe64c6d77..90021a034a3d3 100644 --- a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr +++ b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead. +error: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead error: aborting due to previous error From 499813026f1e84456c9a108b249ed1005c624181 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sun, 2 May 2021 17:50:22 -0500 Subject: [PATCH 22/67] Fix config parsing --- clippy_lints/src/utils/conf.rs | 21 ++++++++++----------- util/lintlib.py | 2 +- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index aeca5b9cccee4..dc079663fc97a 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -28,7 +28,7 @@ macro_rules! define_Conf { ($( #[$doc:meta] $(#[conf_deprecated($dep:literal)])? - ($name:ident: $ty:ty $(= $default:expr)?), + ($name:ident: $ty:ty = $default:expr), )*) => { /// Clippy lint configuration pub struct Conf { @@ -36,7 +36,7 @@ macro_rules! define_Conf { } mod defaults { - $(pub fn $name() -> $ty { define_Conf!(@default $($default)?) })* + $(pub fn $name() -> $ty { $default })* } impl Default for Conf { @@ -90,20 +90,19 @@ macro_rules! define_Conf { } } }; - (@default) => (Default::default()); - (@default $default:expr) => ($default); } +// N.B., this macro is parsed by util/lintlib.py define_Conf! { /// Lint: 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. The minimum rust version that the project supports - (msrv: Option), + (msrv: Option = 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 = ["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), + (cyclomatic_complexity_threshold: Option = None), /// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks (doc_valid_idents: Vec = [ "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", @@ -142,7 +141,7 @@ define_Conf! { /// 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), + (trivial_copy_size_limit: Option = 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 @@ -158,15 +157,15 @@ define_Conf! { /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters 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), + (warn_on_all_wildcard_imports: bool = false), /// Lint: DISALLOWED_METHOD. The list of disallowed methods, written as fully qualified paths. - (disallowed_methods: Vec), + (disallowed_methods: Vec = 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), + (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), + (cargo_ignore_publish: bool = false), } /// Search for the configuration file. diff --git a/util/lintlib.py b/util/lintlib.py index d0d9beb9b2d9f..5707cf0ce0f8c 100644 --- a/util/lintlib.py +++ b/util/lintlib.py @@ -14,7 +14,7 @@ group_re = re.compile(r'''\s*([a-z_][a-z_0-9]+)''') conf_re = re.compile(r'''define_Conf! {\n([^}]*)\n}''', re.MULTILINE) confvar_re = re.compile( - r'''/// Lint: ([\w,\s]+)\. (.*)\n\s*\([^,]+,\s+"([^"]+)":\s+([^,]+),\s+([^\.\)]+).*\),''', re.MULTILINE) + r'''/// Lint: ([\w,\s]+)\. (.*)\n\s*\(([^:]+):\s*([^\s=]+)\s*=\s*([^\.\)]+).*\),''', re.MULTILINE) comment_re = re.compile(r'''\s*/// ?(.*)''') lint_levels = { From ffb0951a4d80cda686349f8f735ac4ac8a0e0cb4 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sun, 2 May 2021 17:53:01 -0500 Subject: [PATCH 23/67] Update config contributing docs --- doc/adding_lints.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/adding_lints.md b/doc/adding_lints.md index d6cc6d0c2c763..5a06afedbf4c2 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -454,7 +454,7 @@ in `clippy_lints/src/utils/conf.rs`: ```rust define_Conf! { /// Lint: LIST, OF, LINTS, . The minimum rust version that the project supports - (msrv, "msrv": Option, None), + (msrv: Option = None), ... } ``` @@ -562,7 +562,7 @@ in the following steps: like this: ```rust /// Lint: LINT_NAME. - (configuration_ident, "configuration_value": Type, DefaultValue), + (configuration_ident: Type = DefaultValue), ``` The configuration value and identifier should usually be the same. The doc comment will be automatically added to the lint documentation. From c0808998481402cb1cd502ad6aa54e253840efc6 Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 3 May 2021 16:18:41 +0200 Subject: [PATCH 24/67] add strip_{prefix,suffix} to PATTERN_METHODS this will warn, if a single_char_pattern is used in one of the above methods --- clippy_lints/src/methods/mod.rs | 4 +++- tests/ui/single_char_pattern.fixed | 2 ++ tests/ui/single_char_pattern.rs | 2 ++ tests/ui/single_char_pattern.stderr | 34 +++++++++++++++++++---------- 4 files changed, 30 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index e15dbb899b36a..5ea989aa684be 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2190,7 +2190,7 @@ const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ ]; #[rustfmt::skip] -const PATTERN_METHODS: [(&str, usize); 17] = [ +const PATTERN_METHODS: [(&str, usize); 19] = [ ("contains", 1), ("starts_with", 1), ("ends_with", 1), @@ -2206,6 +2206,8 @@ const PATTERN_METHODS: [(&str, usize); 17] = [ ("rmatches", 1), ("match_indices", 1), ("rmatch_indices", 1), + ("strip_prefix", 1), + ("strip_suffix", 1), ("trim_start_matches", 1), ("trim_end_matches", 1), ]; diff --git a/tests/ui/single_char_pattern.fixed b/tests/ui/single_char_pattern.fixed index d8b5f19e144dc..fcbe9af9f5616 100644 --- a/tests/ui/single_char_pattern.fixed +++ b/tests/ui/single_char_pattern.fixed @@ -33,6 +33,8 @@ fn main() { x.rmatch_indices('x'); x.trim_start_matches('x'); x.trim_end_matches('x'); + x.strip_prefix('x'); + x.strip_suffix('x'); // Make sure we escape characters correctly. x.split('\n'); x.split('\''); diff --git a/tests/ui/single_char_pattern.rs b/tests/ui/single_char_pattern.rs index a7bc73e3756df..b8bc20f4070fc 100644 --- a/tests/ui/single_char_pattern.rs +++ b/tests/ui/single_char_pattern.rs @@ -33,6 +33,8 @@ fn main() { x.rmatch_indices("x"); x.trim_start_matches("x"); x.trim_end_matches("x"); + x.strip_prefix("x"); + x.strip_suffix("x"); // Make sure we escape characters correctly. x.split("\n"); x.split("'"); diff --git a/tests/ui/single_char_pattern.stderr b/tests/ui/single_char_pattern.stderr index ee4e7e50efd17..6d94d8a34e390 100644 --- a/tests/ui/single_char_pattern.stderr +++ b/tests/ui/single_char_pattern.stderr @@ -121,64 +121,76 @@ LL | x.trim_end_matches("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:37:13 + --> $DIR/single_char_pattern.rs:36:20 + | +LL | x.strip_prefix("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:37:20 + | +LL | x.strip_suffix("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:39:13 | LL | x.split("/n"); | ^^^^ help: try using a `char` instead: `'/n'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:38:13 + --> $DIR/single_char_pattern.rs:40:13 | LL | x.split("'"); | ^^^ help: try using a `char` instead: `'/''` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:39:13 + --> $DIR/single_char_pattern.rs:41:13 | LL | x.split("/'"); | ^^^^ help: try using a `char` instead: `'/''` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:44:31 + --> $DIR/single_char_pattern.rs:46:31 | LL | x.replace(";", ",").split(","); // issue #2978 | ^^^ help: try using a `char` instead: `','` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:45:19 + --> $DIR/single_char_pattern.rs:47:19 | LL | x.starts_with("/x03"); // issue #2996 | ^^^^^^ help: try using a `char` instead: `'/x03'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:52:13 + --> $DIR/single_char_pattern.rs:54:13 | LL | x.split(r"a"); | ^^^^ help: try using a `char` instead: `'a'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:53:13 + --> $DIR/single_char_pattern.rs:55:13 | LL | x.split(r#"a"#); | ^^^^^^ help: try using a `char` instead: `'a'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:54:13 + --> $DIR/single_char_pattern.rs:56:13 | LL | x.split(r###"a"###); | ^^^^^^^^^^ help: try using a `char` instead: `'a'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:55:13 + --> $DIR/single_char_pattern.rs:57:13 | LL | x.split(r###"'"###); | ^^^^^^^^^^ help: try using a `char` instead: `'/''` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:56:13 + --> $DIR/single_char_pattern.rs:58:13 | LL | x.split(r###"#"###); | ^^^^^^^^^^ help: try using a `char` instead: `'#'` -error: aborting due to 30 previous errors +error: aborting due to 32 previous errors From 19e7448c7f6a4e3a07507c2742bef07d56f1a4a3 Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 3 May 2021 16:21:27 +0200 Subject: [PATCH 25/67] move PATTERN_METHODS table directly into file also removed rustfmt::skip --- clippy_lints/src/methods/mod.rs | 23 ------------------ .../src/methods/single_char_pattern.rs | 24 ++++++++++++++++++- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 5ea989aa684be..0b1b6304defcb 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2189,29 +2189,6 @@ const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ ShouldImplTraitCase::new("std::ops::Sub", "sub", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), ]; -#[rustfmt::skip] -const PATTERN_METHODS: [(&str, usize); 19] = [ - ("contains", 1), - ("starts_with", 1), - ("ends_with", 1), - ("find", 1), - ("rfind", 1), - ("split", 1), - ("rsplit", 1), - ("split_terminator", 1), - ("rsplit_terminator", 1), - ("splitn", 2), - ("rsplitn", 2), - ("matches", 1), - ("rmatches", 1), - ("match_indices", 1), - ("rmatch_indices", 1), - ("strip_prefix", 1), - ("strip_suffix", 1), - ("trim_start_matches", 1), - ("trim_end_matches", 1), -]; - #[derive(Clone, Copy, PartialEq, Debug)] enum SelfKind { Value, diff --git a/clippy_lints/src/methods/single_char_pattern.rs b/clippy_lints/src/methods/single_char_pattern.rs index f4090c7c617d4..d313a3db479de 100644 --- a/clippy_lints/src/methods/single_char_pattern.rs +++ b/clippy_lints/src/methods/single_char_pattern.rs @@ -9,9 +9,31 @@ use rustc_span::symbol::Symbol; use super::SINGLE_CHAR_PATTERN; +const PATTERN_METHODS: [(&str, usize); 19] = [ + ("contains", 1), + ("starts_with", 1), + ("ends_with", 1), + ("find", 1), + ("rfind", 1), + ("split", 1), + ("rsplit", 1), + ("split_terminator", 1), + ("rsplit_terminator", 1), + ("splitn", 2), + ("rsplitn", 2), + ("matches", 1), + ("rmatches", 1), + ("match_indices", 1), + ("rmatch_indices", 1), + ("strip_prefix", 1), + ("strip_suffix", 1), + ("trim_start_matches", 1), + ("trim_end_matches", 1), +]; + /// lint for length-1 `str`s for methods in `PATTERN_METHODS` pub(super) fn check(cx: &LateContext<'_>, _expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) { - for &(method, pos) in &crate::methods::PATTERN_METHODS { + for &(method, pos) in &PATTERN_METHODS { if_chain! { if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(&args[0]).kind(); if *ty.kind() == ty::Str; From ee6d1a35ba29948fc483426fee36aa5bc9aedd97 Mon Sep 17 00:00:00 2001 From: Christiaan Dirkx Date: Sun, 25 Apr 2021 13:10:19 +0200 Subject: [PATCH 26/67] Change `std::sys::unix::ext::fs::PermissionsExt::from_mode` to `std::os::imp::unix::fs::PermissionsExt::from_mode` in Clippy --- clippy_utils/src/paths.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 5e6733a300f2c..1fa439639b24a 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -101,7 +101,7 @@ pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 2] = ["parking_lot", "RwLockWri pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"]; pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"]; pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"]; -pub const PERMISSIONS_FROM_MODE: [&str; 7] = ["std", "sys", "unix", "ext", "fs", "PermissionsExt", "from_mode"]; +pub const PERMISSIONS_FROM_MODE: [&str; 7] = ["std", "os", "imp", "unix", "fs", "PermissionsExt", "from_mode"]; pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"]; pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"]; pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"]; From 91a8611b4426c7da8c72317516626affb812d20b Mon Sep 17 00:00:00 2001 From: Takayuki Date: Tue, 4 May 2021 16:38:34 +0900 Subject: [PATCH 27/67] move misc_early to misc_early/mod.rs --- clippy_lints/src/{misc_early.rs => misc_early/mod.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename clippy_lints/src/{misc_early.rs => misc_early/mod.rs} (100%) diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early/mod.rs similarity index 100% rename from clippy_lints/src/misc_early.rs rename to clippy_lints/src/misc_early/mod.rs From 64eb18e6759f4375d01d7e3352447ff6b4305184 Mon Sep 17 00:00:00 2001 From: Takayuki Date: Tue, 4 May 2021 17:04:10 +0900 Subject: [PATCH 28/67] move builtin_type_shadow to its own module --- .../src/misc_early/builtin_type_shadow.rs | 19 +++++++++++++++++++ clippy_lints/src/misc_early/mod.rs | 18 +++++------------- ...-type-shadow.rs => builtin_type_shadow.rs} | 0 ...adow.stderr => builtin_type_shadow.stderr} | 4 ++-- 4 files changed, 26 insertions(+), 15 deletions(-) create mode 100644 clippy_lints/src/misc_early/builtin_type_shadow.rs rename tests/ui/{builtin-type-shadow.rs => builtin_type_shadow.rs} (100%) rename tests/ui/{builtin-type-shadow.stderr => builtin_type_shadow.stderr} (89%) diff --git a/clippy_lints/src/misc_early/builtin_type_shadow.rs b/clippy_lints/src/misc_early/builtin_type_shadow.rs new file mode 100644 index 0000000000000..9f6b0bdc7a45a --- /dev/null +++ b/clippy_lints/src/misc_early/builtin_type_shadow.rs @@ -0,0 +1,19 @@ +use clippy_utils::diagnostics::span_lint; +use rustc_ast::ast::{GenericParam, GenericParamKind}; +use rustc_hir::PrimTy; +use rustc_lint::EarlyContext; + +use super::BUILTIN_TYPE_SHADOW; + +pub(super) fn check(cx: &EarlyContext<'_>, param: &GenericParam) { + if let GenericParamKind::Type { .. } = param.kind { + if let Some(prim_ty) = PrimTy::from_name(param.ident.name) { + span_lint( + cx, + BUILTIN_TYPE_SHADOW, + param.ident.span, + &format!("this generic shadows the built-in type `{}`", prim_ty.name()), + ); + } + } +} diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index 3c6a7071c24a7..94740093d3b1e 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -1,13 +1,14 @@ +mod builtin_type_shadow; + use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_opt; use rustc_ast::ast::{ - BindingMode, Expr, ExprKind, GenericParamKind, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability, - NodeId, Pat, PatKind, UnOp, + BindingMode, Expr, ExprKind, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability, NodeId, Pat, PatKind, + UnOp, }; use rustc_ast::visit::FnKind; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; -use rustc_hir::PrimTy; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -265,16 +266,7 @@ declare_lint_pass!(MiscEarlyLints => [ impl EarlyLintPass for MiscEarlyLints { fn check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics) { for param in &gen.params { - if let GenericParamKind::Type { .. } = param.kind { - if let Some(prim_ty) = PrimTy::from_name(param.ident.name) { - span_lint( - cx, - BUILTIN_TYPE_SHADOW, - param.ident.span, - &format!("this generic shadows the built-in type `{}`", prim_ty.name()), - ); - } - } + builtin_type_shadow::check(cx, param); } } diff --git a/tests/ui/builtin-type-shadow.rs b/tests/ui/builtin_type_shadow.rs similarity index 100% rename from tests/ui/builtin-type-shadow.rs rename to tests/ui/builtin_type_shadow.rs diff --git a/tests/ui/builtin-type-shadow.stderr b/tests/ui/builtin_type_shadow.stderr similarity index 89% rename from tests/ui/builtin-type-shadow.stderr rename to tests/ui/builtin_type_shadow.stderr index f42b246afd293..47a8a1e623e8a 100644 --- a/tests/ui/builtin-type-shadow.stderr +++ b/tests/ui/builtin_type_shadow.stderr @@ -1,5 +1,5 @@ error: this generic shadows the built-in type `u32` - --> $DIR/builtin-type-shadow.rs:4:8 + --> $DIR/builtin_type_shadow.rs:4:8 | LL | fn foo(a: u32) -> u32 { | ^^^ @@ -7,7 +7,7 @@ LL | fn foo(a: u32) -> u32 { = note: `-D clippy::builtin-type-shadow` implied by `-D warnings` error[E0308]: mismatched types - --> $DIR/builtin-type-shadow.rs:5:5 + --> $DIR/builtin_type_shadow.rs:5:5 | LL | fn foo(a: u32) -> u32 { | --- --- expected `u32` because of return type From 55af0cee15cf5c85cbb50ef41eafb4498bde24e3 Mon Sep 17 00:00:00 2001 From: Takayuki Date: Tue, 4 May 2021 17:20:22 +0900 Subject: [PATCH 29/67] move double_neg to its own module --- clippy_lints/src/misc_early/double_neg.rs | 23 +++++++++++++++++++++++ clippy_lints/src/misc_early/mod.rs | 19 +++---------------- 2 files changed, 26 insertions(+), 16 deletions(-) create mode 100644 clippy_lints/src/misc_early/double_neg.rs diff --git a/clippy_lints/src/misc_early/double_neg.rs b/clippy_lints/src/misc_early/double_neg.rs new file mode 100644 index 0000000000000..6f65778e1193c --- /dev/null +++ b/clippy_lints/src/misc_early/double_neg.rs @@ -0,0 +1,23 @@ +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), + _ => (), + } +} diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index 94740093d3b1e..47cc87dfffb9d 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -1,10 +1,10 @@ mod builtin_type_shadow; +mod double_neg; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_opt; use rustc_ast::ast::{ - BindingMode, Expr, ExprKind, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability, NodeId, Pat, PatKind, - UnOp, + BindingMode, Expr, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability, NodeId, Pat, PatKind, }; use rustc_ast::visit::FnKind; use rustc_data_structures::fx::FxHashMap; @@ -393,20 +393,7 @@ impl EarlyLintPass for MiscEarlyLints { if in_external_macro(cx.sess(), expr.span) { return; } - 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) => Self::check_lit(cx, lit), - _ => (), - } + double_neg::check(cx, expr) } } From 52cfde058668ddce3a191c0c608ac07603175d04 Mon Sep 17 00:00:00 2001 From: Takayuki Date: Tue, 4 May 2021 17:28:32 +0900 Subject: [PATCH 30/67] move redundant_pattern to its own module --- clippy_lints/src/misc_early/mod.rs | 29 ++--------------- .../src/misc_early/redundant_pattern.rs | 31 +++++++++++++++++++ 2 files changed, 34 insertions(+), 26 deletions(-) create mode 100644 clippy_lints/src/misc_early/redundant_pattern.rs diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index 47cc87dfffb9d..564afdb7f8d4c 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -1,11 +1,10 @@ mod builtin_type_shadow; mod double_neg; +mod redundant_pattern; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_opt; -use rustc_ast::ast::{ - BindingMode, Expr, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability, NodeId, Pat, PatKind, -}; +use rustc_ast::ast::{Expr, Generics, Lit, LitFloatType, LitIntType, LitKind, NodeId, Pat, PatKind}; use rustc_ast::visit::FnKind; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; @@ -336,29 +335,7 @@ impl EarlyLintPass for MiscEarlyLints { } } - if let PatKind::Ident(left, ident, Some(ref right)) = pat.kind { - let left_binding = match left { - BindingMode::ByRef(Mutability::Mut) => "ref mut ", - BindingMode::ByRef(Mutability::Not) => "ref ", - BindingMode::ByValue(..) => "", - }; - - if let PatKind::Wild = right.kind { - span_lint_and_sugg( - cx, - REDUNDANT_PATTERN, - pat.span, - &format!( - "the `{} @ _` pattern can be written as just `{}`", - ident.name, ident.name, - ), - "try", - format!("{}{}", left_binding, ident.name), - Applicability::MachineApplicable, - ); - } - } - + redundant_pattern::check(cx, pat); check_unneeded_wildcard_pattern(cx, pat); } diff --git a/clippy_lints/src/misc_early/redundant_pattern.rs b/clippy_lints/src/misc_early/redundant_pattern.rs new file mode 100644 index 0000000000000..525dbf7757c14 --- /dev/null +++ b/clippy_lints/src/misc_early/redundant_pattern.rs @@ -0,0 +1,31 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use rustc_ast::ast::{BindingMode, Mutability, Pat, PatKind}; +use rustc_errors::Applicability; +use rustc_lint::EarlyContext; + +use super::REDUNDANT_PATTERN; + +pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { + if let PatKind::Ident(left, ident, Some(ref right)) = pat.kind { + let left_binding = match left { + BindingMode::ByRef(Mutability::Mut) => "ref mut ", + BindingMode::ByRef(Mutability::Not) => "ref ", + BindingMode::ByValue(..) => "", + }; + + if let PatKind::Wild = right.kind { + span_lint_and_sugg( + cx, + REDUNDANT_PATTERN, + pat.span, + &format!( + "the `{} @ _` pattern can be written as just `{}`", + ident.name, ident.name, + ), + "try", + format!("{}{}", left_binding, ident.name), + Applicability::MachineApplicable, + ); + } + } +} From c0a106e25214a16cb01c87f7f01fd7c95d3534c2 Mon Sep 17 00:00:00 2001 From: Takayuki Date: Tue, 4 May 2021 19:50:48 +0900 Subject: [PATCH 31/67] move unneeded_wildcard_pattern to its own module --- clippy_lints/src/misc_early/mod.rs | 48 +---------------- .../misc_early/unneeded_wildcard_pattern.rs | 52 +++++++++++++++++++ 2 files changed, 54 insertions(+), 46 deletions(-) create mode 100644 clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index 564afdb7f8d4c..7d1cd81f49f2a 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -1,6 +1,7 @@ mod builtin_type_shadow; mod double_neg; mod redundant_pattern; +mod unneeded_wildcard_pattern; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_opt; @@ -336,7 +337,7 @@ impl EarlyLintPass for MiscEarlyLints { } redundant_pattern::check(cx, pat); - check_unneeded_wildcard_pattern(cx, pat); + unneeded_wildcard_pattern::check(cx, pat); } fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) { @@ -478,48 +479,3 @@ impl MiscEarlyLints { } } } - -fn check_unneeded_wildcard_pattern(cx: &EarlyContext<'_>, pat: &Pat) { - if let PatKind::TupleStruct(_, ref patterns) | PatKind::Tuple(ref patterns) = pat.kind { - fn span_lint(cx: &EarlyContext<'_>, span: Span, only_one: bool) { - span_lint_and_sugg( - cx, - UNNEEDED_WILDCARD_PATTERN, - span, - if only_one { - "this pattern is unneeded as the `..` pattern can match that element" - } else { - "these patterns are unneeded as the `..` pattern can match those elements" - }, - if only_one { "remove it" } else { "remove them" }, - "".to_string(), - Applicability::MachineApplicable, - ); - } - - if let Some(rest_index) = patterns.iter().position(|pat| pat.is_rest()) { - if let Some((left_index, left_pat)) = patterns[..rest_index] - .iter() - .rev() - .take_while(|pat| matches!(pat.kind, PatKind::Wild)) - .enumerate() - .last() - { - span_lint(cx, left_pat.span.until(patterns[rest_index].span), left_index == 0); - } - - if let Some((right_index, right_pat)) = patterns[rest_index + 1..] - .iter() - .take_while(|pat| matches!(pat.kind, PatKind::Wild)) - .enumerate() - .last() - { - span_lint( - cx, - patterns[rest_index].span.shrink_to_hi().to(right_pat.span), - right_index == 0, - ); - } - } - } -} diff --git a/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs b/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs new file mode 100644 index 0000000000000..4dd032d78f1d5 --- /dev/null +++ b/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs @@ -0,0 +1,52 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use rustc_ast::ast::{Pat, PatKind}; +use rustc_errors::Applicability; +use rustc_lint::EarlyContext; +use rustc_span::source_map::Span; + +use super::UNNEEDED_WILDCARD_PATTERN; + +pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { + if let PatKind::TupleStruct(_, ref patterns) | PatKind::Tuple(ref patterns) = pat.kind { + if let Some(rest_index) = patterns.iter().position(|pat| pat.is_rest()) { + if let Some((left_index, left_pat)) = patterns[..rest_index] + .iter() + .rev() + .take_while(|pat| matches!(pat.kind, PatKind::Wild)) + .enumerate() + .last() + { + span_lint(cx, left_pat.span.until(patterns[rest_index].span), left_index == 0); + } + + if let Some((right_index, right_pat)) = patterns[rest_index + 1..] + .iter() + .take_while(|pat| matches!(pat.kind, PatKind::Wild)) + .enumerate() + .last() + { + span_lint( + cx, + patterns[rest_index].span.shrink_to_hi().to(right_pat.span), + right_index == 0, + ); + } + } + } +} + +fn span_lint(cx: &EarlyContext<'_>, span: Span, only_one: bool) { + span_lint_and_sugg( + cx, + UNNEEDED_WILDCARD_PATTERN, + span, + if only_one { + "this pattern is unneeded as the `..` pattern can match that element" + } else { + "these patterns are unneeded as the `..` pattern can match those elements" + }, + if only_one { "remove it" } else { "remove them" }, + "".to_string(), + Applicability::MachineApplicable, + ); +} From 1c3c54a6effc99f21db3df597a6e4a3531c25a86 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 4 May 2021 08:43:15 -0500 Subject: [PATCH 32/67] SpanlessHash Pat --- clippy_utils/src/hir_utils.rs | 93 +++++++++++++++++++++++++++++------ 1 file changed, 78 insertions(+), 15 deletions(-) diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 07ae6e924e28b..3b01158acd91f 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -713,7 +713,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_expr(e); for arm in arms { - // TODO: arm.pat? + self.hash_pat(arm.pat); if let Some(ref e) = arm.guard { self.hash_guard(e); } @@ -791,6 +791,72 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { // self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s); } + pub fn hash_pat(&mut self, pat: &Pat<'_>) { + std::mem::discriminant(&pat.kind).hash(&mut self.s); + match pat.kind { + PatKind::Binding(ann, _, _, pat) => { + ann.hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s); + if let Some(pat) = pat { + self.hash_pat(pat); + } + }, + PatKind::Box(pat) => self.hash_pat(pat), + PatKind::Lit(expr) => self.hash_expr(expr), + PatKind::Or(pats) => { + for pat in pats { + self.hash_pat(pat); + } + }, + PatKind::Path(ref qpath) => self.hash_qpath(qpath), + PatKind::Range(s, e, i) => { + if let Some(s) = s { + self.hash_expr(s); + } + if let Some(e) = e { + self.hash_expr(e); + } + i.hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s); + }, + PatKind::Ref(pat, m) => { + self.hash_pat(pat); + m.hash(&mut self.s); + }, + PatKind::Slice(l, m, r) => { + for pat in l { + self.hash_pat(pat); + } + if let Some(pat) = m { + self.hash_pat(pat); + } + for pat in r { + self.hash_pat(pat); + } + }, + PatKind::Struct(ref qpath, fields, e) => { + self.hash_qpath(qpath); + for f in fields { + self.hash_name(f.ident.name); + self.hash_pat(f.pat); + } + e.hash(&mut self.s) + }, + PatKind::Tuple(pats, e) => { + for pat in pats { + self.hash_pat(pat); + } + e.hash(&mut self.s); + }, + PatKind::TupleStruct(ref qpath, pats, e) => { + self.hash_qpath(qpath); + for pat in pats { + self.hash_pat(pat); + } + e.hash(&mut self.s); + }, + PatKind::Wild => {}, + } + } + pub fn hash_path(&mut self, path: &Path<'_>) { match path.res { // constant hash since equality is dependant on inter-expression context @@ -808,6 +874,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { match &b.kind { StmtKind::Local(local) => { + self.hash_pat(local.pat); if let Some(ref init) = local.init { self.hash_expr(init); } @@ -827,7 +894,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } } - pub fn hash_lifetime(&mut self, lifetime: &Lifetime) { + pub fn hash_lifetime(&mut self, lifetime: Lifetime) { std::mem::discriminant(&lifetime.name).hash(&mut self.s); if let LifetimeName::Param(ref name) = lifetime.name { std::mem::discriminant(name).hash(&mut self.s); @@ -844,12 +911,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } pub fn hash_ty(&mut self, ty: &Ty<'_>) { - self.hash_tykind(&ty.kind); - } - - pub fn hash_tykind(&mut self, ty: &TyKind<'_>) { - std::mem::discriminant(ty).hash(&mut self.s); - match ty { + std::mem::discriminant(&ty.kind).hash(&mut self.s); + match ty.kind { TyKind::Slice(ty) => { self.hash_ty(ty); }, @@ -857,11 +920,11 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_ty(ty); self.hash_body(anon_const.body); }, - TyKind::Ptr(mut_ty) => { + TyKind::Ptr(ref mut_ty) => { self.hash_ty(&mut_ty.ty); mut_ty.mutbl.hash(&mut self.s); }, - TyKind::Rptr(lifetime, mut_ty) => { + TyKind::Rptr(lifetime, ref mut_ty) => { self.hash_lifetime(lifetime); self.hash_ty(&mut_ty.ty); mut_ty.mutbl.hash(&mut self.s); @@ -883,11 +946,11 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { bfn.decl.c_variadic.hash(&mut self.s); }, TyKind::Tup(ty_list) => { - for ty in *ty_list { + for ty in ty_list { self.hash_ty(ty); } }, - TyKind::Path(qpath) => match qpath { + TyKind::Path(ref qpath) => match qpath { QPath::Resolved(ref maybe_ty, ref path) => { if let Some(ref ty) = maybe_ty { self.hash_ty(ty); @@ -927,9 +990,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { fn hash_generic_args(&mut self, arg_list: &[GenericArg<'_>]) { for arg in arg_list { - match arg { - GenericArg::Lifetime(ref l) => self.hash_lifetime(l), - GenericArg::Type(ref ty) => self.hash_ty(&ty), + match *arg { + GenericArg::Lifetime(l) => self.hash_lifetime(l), + GenericArg::Type(ref ty) => self.hash_ty(ty), GenericArg::Const(ref ca) => self.hash_body(ca.value.body), } } From 829fde5e1f1163b3218409244863ce64d8c7dd19 Mon Sep 17 00:00:00 2001 From: Takayuki Date: Wed, 5 May 2021 01:44:38 +0900 Subject: [PATCH 33/67] move unneeded_field_pattern to its own module --- clippy_lints/src/misc_early/mod.rs | 69 +----------------- .../src/misc_early/unneeded_field_pattern.rs | 72 +++++++++++++++++++ 2 files changed, 75 insertions(+), 66 deletions(-) create mode 100644 clippy_lints/src/misc_early/unneeded_field_pattern.rs diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index 7d1cd81f49f2a..ee1bff8bf8aaa 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -1,9 +1,10 @@ mod builtin_type_shadow; mod double_neg; mod redundant_pattern; +mod unneeded_field_pattern; mod unneeded_wildcard_pattern; -use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_opt; use rustc_ast::ast::{Expr, Generics, Lit, LitFloatType, LitIntType, LitKind, NodeId, Pat, PatKind}; use rustc_ast::visit::FnKind; @@ -271,71 +272,7 @@ impl EarlyLintPass for MiscEarlyLints { } fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) { - if let PatKind::Struct(ref npat, ref pfields, _) = pat.kind { - let mut wilds = 0; - let type_name = npat - .segments - .last() - .expect("A path must have at least one segment") - .ident - .name; - - for field in pfields { - if let PatKind::Wild = field.pat.kind { - wilds += 1; - } - } - if !pfields.is_empty() && wilds == pfields.len() { - span_lint_and_help( - cx, - UNNEEDED_FIELD_PATTERN, - pat.span, - "all the struct fields are matched to a wildcard pattern, consider using `..`", - None, - &format!("try with `{} {{ .. }}` instead", type_name), - ); - return; - } - if wilds > 0 { - for field in pfields { - if let PatKind::Wild = field.pat.kind { - wilds -= 1; - if wilds > 0 { - span_lint( - cx, - UNNEEDED_FIELD_PATTERN, - field.span, - "you matched a field with a wildcard pattern, consider using `..` instead", - ); - } else { - let mut normal = vec![]; - - for field in pfields { - match field.pat.kind { - PatKind::Wild => {}, - _ => { - if let Ok(n) = cx.sess().source_map().span_to_snippet(field.span) { - normal.push(n); - } - }, - } - } - - span_lint_and_help( - cx, - UNNEEDED_FIELD_PATTERN, - field.span, - "you matched a field with a wildcard pattern, consider using `..` \ - instead", - None, - &format!("try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")), - ); - } - } - } - } - } - + unneeded_field_pattern::check(cx, pat); redundant_pattern::check(cx, pat); unneeded_wildcard_pattern::check(cx, pat); } diff --git a/clippy_lints/src/misc_early/unneeded_field_pattern.rs b/clippy_lints/src/misc_early/unneeded_field_pattern.rs new file mode 100644 index 0000000000000..329a0009a3e2c --- /dev/null +++ b/clippy_lints/src/misc_early/unneeded_field_pattern.rs @@ -0,0 +1,72 @@ +use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use rustc_ast::ast::{Pat, PatKind}; +use rustc_lint::{EarlyContext, LintContext}; + +use super::UNNEEDED_FIELD_PATTERN; + +pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { + if let PatKind::Struct(ref npat, ref pfields, _) = pat.kind { + let mut wilds = 0; + let type_name = npat + .segments + .last() + .expect("A path must have at least one segment") + .ident + .name; + + for field in pfields { + if let PatKind::Wild = field.pat.kind { + wilds += 1; + } + } + if !pfields.is_empty() && wilds == pfields.len() { + span_lint_and_help( + cx, + UNNEEDED_FIELD_PATTERN, + pat.span, + "all the struct fields are matched to a wildcard pattern, consider using `..`", + None, + &format!("try with `{} {{ .. }}` instead", type_name), + ); + return; + } + if wilds > 0 { + for field in pfields { + if let PatKind::Wild = field.pat.kind { + wilds -= 1; + if wilds > 0 { + span_lint( + cx, + UNNEEDED_FIELD_PATTERN, + field.span, + "you matched a field with a wildcard pattern, consider using `..` instead", + ); + } else { + let mut normal = vec![]; + + for field in pfields { + match field.pat.kind { + PatKind::Wild => {}, + _ => { + if let Ok(n) = cx.sess().source_map().span_to_snippet(field.span) { + normal.push(n); + } + }, + } + } + + span_lint_and_help( + cx, + UNNEEDED_FIELD_PATTERN, + field.span, + "you matched a field with a wildcard pattern, consider using `..` \ + instead", + None, + &format!("try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")), + ); + } + } + } + } + } +} From f012e0e3de04ca5b8022a0b8856a12d47d746b5b Mon Sep 17 00:00:00 2001 From: Takayuki Date: Wed, 5 May 2021 02:10:24 +0900 Subject: [PATCH 34/67] move unseparated_literal_suffix to its own module --- clippy_lints/src/misc_early/mod.rs | 19 ++----------- .../misc_early/unseparated_literal_suffix.rs | 27 +++++++++++++++++++ 2 files changed, 29 insertions(+), 17 deletions(-) create mode 100644 clippy_lints/src/misc_early/unseparated_literal_suffix.rs diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index ee1bff8bf8aaa..182218e77f64d 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -3,6 +3,7 @@ mod double_neg; mod redundant_pattern; mod unneeded_field_pattern; mod unneeded_wildcard_pattern; +mod unseparated_literal_suffix; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_opt; @@ -396,23 +397,7 @@ impl MiscEarlyLints { ); } } else if let LitKind::Float(_, LitFloatType::Suffixed(float_ty)) = lit.kind { - let suffix = float_ty.name_str(); - let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) { - val - } else { - return; // It's useless so shouldn't lint. - }; - if lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' { - span_lint_and_sugg( - cx, - UNSEPARATED_LITERAL_SUFFIX, - lit.span, - "float type suffix should be separated by an underscore", - "add an underscore", - format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix), - Applicability::MachineApplicable, - ); - } + unseparated_literal_suffix::check(cx, lit, float_ty, lit_snip) } } } diff --git a/clippy_lints/src/misc_early/unseparated_literal_suffix.rs b/clippy_lints/src/misc_early/unseparated_literal_suffix.rs new file mode 100644 index 0000000000000..ffdd5d93a38c6 --- /dev/null +++ b/clippy_lints/src/misc_early/unseparated_literal_suffix.rs @@ -0,0 +1,27 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use rustc_ast::ast::FloatTy; +use rustc_ast::ast::Lit; +use rustc_errors::Applicability; +use rustc_lint::EarlyContext; + +use super::UNSEPARATED_LITERAL_SUFFIX; + +pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, float_ty: FloatTy, lit_snip: String) { + let suffix = float_ty.name_str(); + let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) { + val + } else { + return; // It's useless so shouldn't lint. + }; + if lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' { + span_lint_and_sugg( + cx, + UNSEPARATED_LITERAL_SUFFIX, + lit.span, + "float type suffix should be separated by an underscore", + "add an underscore", + format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix), + Applicability::MachineApplicable, + ); + } +} From 0773d8afdb792a3147de53ad590b620be9eef006 Mon Sep 17 00:00:00 2001 From: Takayuki Date: Wed, 5 May 2021 02:21:26 +0900 Subject: [PATCH 35/67] move mixed_case_hex_literals to its own module --- .../src/misc_early/mixed_case_hex_literals.rs | 29 +++++++++++++++++++ clippy_lints/src/misc_early/mod.rs | 23 ++------------- 2 files changed, 31 insertions(+), 21 deletions(-) create mode 100644 clippy_lints/src/misc_early/mixed_case_hex_literals.rs diff --git a/clippy_lints/src/misc_early/mixed_case_hex_literals.rs b/clippy_lints/src/misc_early/mixed_case_hex_literals.rs new file mode 100644 index 0000000000000..fbcbfa2556fea --- /dev/null +++ b/clippy_lints/src/misc_early/mixed_case_hex_literals.rs @@ -0,0 +1,29 @@ +use clippy_utils::diagnostics::span_lint; +use rustc_ast::ast::Lit; +use rustc_lint::EarlyContext; + +use super::MIXED_CASE_HEX_LITERALS; + +pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, maybe_last_sep_idx: usize, lit_snip: String) { + if maybe_last_sep_idx <= 2 { + // It's meaningless or causes range error. + return; + } + let mut seen = (false, false); + for ch in lit_snip.as_bytes()[2..=maybe_last_sep_idx].iter() { + match ch { + b'a'..=b'f' => seen.0 = true, + b'A'..=b'F' => seen.1 = true, + _ => {}, + } + if seen.0 && seen.1 { + span_lint( + cx, + MIXED_CASE_HEX_LITERALS, + lit.span, + "inconsistent casing in hexadecimal literal", + ); + break; + } + } +} diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index 182218e77f64d..0bfa6833aacea 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -1,5 +1,6 @@ mod builtin_type_shadow; mod double_neg; +mod mixed_case_hex_literals; mod redundant_pattern; mod unneeded_field_pattern; mod unneeded_wildcard_pattern; @@ -351,27 +352,7 @@ impl MiscEarlyLints { } if lit_snip.starts_with("0x") { - if maybe_last_sep_idx <= 2 { - // It's meaningless or causes range error. - return; - } - let mut seen = (false, false); - for ch in lit_snip.as_bytes()[2..=maybe_last_sep_idx].iter() { - match ch { - b'a'..=b'f' => seen.0 = true, - b'A'..=b'F' => seen.1 = true, - _ => {}, - } - if seen.0 && seen.1 { - span_lint( - cx, - MIXED_CASE_HEX_LITERALS, - lit.span, - "inconsistent casing in hexadecimal literal", - ); - break; - } - } + mixed_case_hex_literals::check(cx, lit, maybe_last_sep_idx, 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') { From b1faaaeb0cfb3310c2679f22a33d5ec855f8aaf8 Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Mon, 3 May 2021 13:36:08 -0700 Subject: [PATCH 36/67] needless_collect: Lint cases with type annotations --- clippy_lints/src/loops/needless_collect.rs | 23 ++++++++--- tests/ui/needless_collect_indirect.rs | 29 +++++++++++++- tests/ui/needless_collect_indirect.stderr | 44 +++++++++++++++++++++- 3 files changed, 89 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index 4d73aef76e87e..293dd8b6b79ee 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -7,9 +7,10 @@ use clippy_utils::{is_trait_method, path_to_local_id, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{Block, Expr, ExprKind, GenericArg, HirId, Local, Pat, PatKind, QPath, StmtKind}; +use rustc_hir::{Block, Expr, ExprKind, GenericArg, GenericArgs, HirId, Local, Pat, PatKind, QPath, StmtKind, Ty}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; + use rustc_span::symbol::{sym, Ident}; use rustc_span::{MultiSpan, Span}; @@ -58,18 +59,30 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont } fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { + fn get_hir_id<'tcx>(ty: Option<&Ty<'tcx>>, method_args: Option<&GenericArgs<'tcx>>) -> Option { + if let Some(ty) = ty { + return Some(ty.hir_id); + } + + if let Some(generic_args) = method_args { + if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0) { + return Some(ty.hir_id); + } + } + + None + } if let ExprKind::Block(block, _) = expr.kind { for stmt in block.stmts { if_chain! { if let StmtKind::Local( Local { pat: Pat { hir_id: pat_id, kind: PatKind::Binding(_, _, ident, .. ), .. }, - init: Some(init_expr), .. } + init: Some(init_expr), ty, .. } ) = stmt.kind; if let ExprKind::MethodCall(method_name, collect_span, &[ref iter_source], ..) = init_expr.kind; if method_name.ident.name == sym!(collect) && is_trait_method(cx, init_expr, sym::Iterator); - if let Some(generic_args) = method_name.args; - if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0); - if let ty = cx.typeck_results().node_type(ty.hir_id); + if let Some(hir_id) = get_hir_id(*ty, method_name.args); + if let ty = cx.typeck_results().node_type(hir_id); if is_type_diagnostic_item(cx, ty, sym::vec_type) || is_type_diagnostic_item(cx, ty, sym::vecdeque_type) || match_type(cx, ty, &paths::LINKED_LIST); diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs index 0918a6868ab4b..2647c6401a463 100644 --- a/tests/ui/needless_collect_indirect.rs +++ b/tests/ui/needless_collect_indirect.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, VecDeque}; +use std::collections::{HashMap, LinkedList, VecDeque}; fn main() { let sample = [1; 5]; @@ -43,3 +43,30 @@ fn main() { .collect::>(); } } + +mod issue7110 { + // #7110 - lint for type annotation cases + use super::*; + + fn lint_vec(string: &str) -> usize { + let buffer: Vec<&str> = string.split('/').collect(); + buffer.len() + } + fn lint_vec_deque() -> usize { + let sample = [1; 5]; + let indirect_len: VecDeque<_> = sample.iter().collect(); + indirect_len.len() + } + fn lint_linked_list() -> usize { + let sample = [1; 5]; + let indirect_len: LinkedList<_> = sample.iter().collect(); + indirect_len.len() + } + fn dont_lint(string: &str) -> usize { + let buffer: Vec<&str> = string.split('/').collect(); + for buff in &buffer { + println!("{}", buff); + } + buffer.len() + } +} diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr index c773b841f3b4b..642beb2865a80 100644 --- a/tests/ui/needless_collect_indirect.stderr +++ b/tests/ui/needless_collect_indirect.stderr @@ -69,5 +69,47 @@ LL | LL | sample.into_iter().any(|x| x == a); | -error: aborting due to 5 previous errors +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:52:51 + | +LL | let buffer: Vec<&str> = string.split('/').collect(); + | ^^^^^^^ +LL | buffer.len() + | ------------ the iterator could be used here instead + | +help: take the original Iterator's count instead of collecting it and finding the length + | +LL | +LL | string.split('/').count() + | + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:57:55 + | +LL | let indirect_len: VecDeque<_> = sample.iter().collect(); + | ^^^^^^^ +LL | indirect_len.len() + | ------------------ the iterator could be used here instead + | +help: take the original Iterator's count instead of collecting it and finding the length + | +LL | +LL | sample.iter().count() + | + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:62:57 + | +LL | let indirect_len: LinkedList<_> = sample.iter().collect(); + | ^^^^^^^ +LL | indirect_len.len() + | ------------------ the iterator could be used here instead + | +help: take the original Iterator's count instead of collecting it and finding the length + | +LL | +LL | sample.iter().count() + | + +error: aborting due to 8 previous errors From 0dc38c047e742d7d3c9f42866305a8c849eb4894 Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Mon, 3 May 2021 23:58:41 -0700 Subject: [PATCH 37/67] Fix copy_iterator uitest --- tests/ui/copy_iterator.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/ui/copy_iterator.rs b/tests/ui/copy_iterator.rs index e3d5928be231b..ae67ebded4374 100644 --- a/tests/ui/copy_iterator.rs +++ b/tests/ui/copy_iterator.rs @@ -16,8 +16,6 @@ impl Iterator for Countdown { fn main() { let my_iterator = Countdown(5); - let a: Vec<_> = my_iterator.take(1).collect(); - assert_eq!(a.len(), 1); - let b: Vec<_> = my_iterator.collect(); - assert_eq!(b.len(), 5); + assert_eq!(my_iterator.take(1).count(), 1); + assert_eq!(my_iterator.count(), 5); } From 1835d8a2383c8ae3d26cdcba594fdb933a41f3fb Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Tue, 4 May 2021 12:36:20 -0700 Subject: [PATCH 38/67] needless_collect: Add `BinaryHeap` for indirect usage lint --- clippy_lints/src/loops/needless_collect.rs | 1 + tests/ui/needless_collect_indirect.rs | 7 ++++++- tests/ui/needless_collect_indirect.stderr | 16 +++++++++++++++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index 293dd8b6b79ee..88d586c934635 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -85,6 +85,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo if let ty = cx.typeck_results().node_type(hir_id); if is_type_diagnostic_item(cx, ty, sym::vec_type) || is_type_diagnostic_item(cx, ty, sym::vecdeque_type) || + is_type_diagnostic_item(cx, ty, sym::BinaryHeap) || match_type(cx, ty, &paths::LINKED_LIST); if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident); if let [iter_call] = &*iter_calls; diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs index 2647c6401a463..2458bf1e490bb 100644 --- a/tests/ui/needless_collect_indirect.rs +++ b/tests/ui/needless_collect_indirect.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, LinkedList, VecDeque}; +use std::collections::{BinaryHeap, HashMap, LinkedList, VecDeque}; fn main() { let sample = [1; 5]; @@ -62,6 +62,11 @@ mod issue7110 { let indirect_len: LinkedList<_> = sample.iter().collect(); indirect_len.len() } + fn lint_binary_heap() -> usize { + let sample = [1; 5]; + let indirect_len: BinaryHeap<_> = sample.iter().collect(); + indirect_len.len() + } fn dont_lint(string: &str) -> usize { let buffer: Vec<&str> = string.split('/').collect(); for buff in &buffer { diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr index 642beb2865a80..f094e182a48f3 100644 --- a/tests/ui/needless_collect_indirect.stderr +++ b/tests/ui/needless_collect_indirect.stderr @@ -111,5 +111,19 @@ LL | LL | sample.iter().count() | -error: aborting due to 8 previous errors +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:67:57 + | +LL | let indirect_len: BinaryHeap<_> = sample.iter().collect(); + | ^^^^^^^ +LL | indirect_len.len() + | ------------------ the iterator could be used here instead + | +help: take the original Iterator's count instead of collecting it and finding the length + | +LL | +LL | sample.iter().count() + | + +error: aborting due to 9 previous errors From b1567f4466388df8377512580d72513e1e4e0e48 Mon Sep 17 00:00:00 2001 From: Takayuki Date: Wed, 5 May 2021 06:18:58 +0900 Subject: [PATCH 39/67] move zero_prefixed_literal to its own module --- clippy_lints/src/misc_early/mod.rs | 24 ++------------- .../src/misc_early/zero_prefixed_literal.rs | 29 +++++++++++++++++++ 2 files changed, 32 insertions(+), 21 deletions(-) create mode 100644 clippy_lints/src/misc_early/zero_prefixed_literal.rs diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index 0bfa6833aacea..4b1d55beca8f5 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -5,8 +5,9 @@ mod redundant_pattern; mod unneeded_field_pattern; mod unneeded_wildcard_pattern; mod unseparated_literal_suffix; +mod zero_prefixed_literal; -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::snippet_opt; use rustc_ast::ast::{Expr, Generics, Lit, LitFloatType, LitIntType, LitKind, NodeId, Pat, PatKind}; use rustc_ast::visit::FnKind; @@ -356,26 +357,7 @@ impl MiscEarlyLints { } else if lit_snip.starts_with("0b") || lit_snip.starts_with("0o") { /* nothing to do */ } else if value != 0 && lit_snip.starts_with('0') { - span_lint_and_then( - cx, - ZERO_PREFIXED_LITERAL, - lit.span, - "this is a decimal constant", - |diag| { - diag.span_suggestion( - lit.span, - "if you mean to use a decimal constant, remove the `0` to avoid confusion", - lit_snip.trim_start_matches(|c| c == '_' || c == '0').to_string(), - Applicability::MaybeIncorrect, - ); - diag.span_suggestion( - lit.span, - "if you mean to use an octal constant, use `0o`", - format!("0o{}", lit_snip.trim_start_matches(|c| c == '_' || c == '0')), - Applicability::MaybeIncorrect, - ); - }, - ); + zero_prefixed_literal::check(cx, lit, lit_snip) } } else if let LitKind::Float(_, LitFloatType::Suffixed(float_ty)) = lit.kind { unseparated_literal_suffix::check(cx, lit, float_ty, lit_snip) diff --git a/clippy_lints/src/misc_early/zero_prefixed_literal.rs b/clippy_lints/src/misc_early/zero_prefixed_literal.rs new file mode 100644 index 0000000000000..16add0fa1b43b --- /dev/null +++ b/clippy_lints/src/misc_early/zero_prefixed_literal.rs @@ -0,0 +1,29 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_ast::ast::Lit; +use rustc_errors::Applicability; +use rustc_lint::EarlyContext; + +use super::ZERO_PREFIXED_LITERAL; + +pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: String) { + span_lint_and_then( + cx, + ZERO_PREFIXED_LITERAL, + lit.span, + "this is a decimal constant", + |diag| { + diag.span_suggestion( + lit.span, + "if you mean to use a decimal constant, remove the `0` to avoid confusion", + lit_snip.trim_start_matches(|c| c == '_' || c == '0').to_string(), + Applicability::MaybeIncorrect, + ); + diag.span_suggestion( + lit.span, + "if you mean to use an octal constant, use `0o`", + format!("0o{}", lit_snip.trim_start_matches(|c| c == '_' || c == '0')), + Applicability::MaybeIncorrect, + ); + }, + ); +} From 2dfb246d8e8db3280326330b9ca67aee5d3214e9 Mon Sep 17 00:00:00 2001 From: Takayuki Date: Wed, 5 May 2021 06:39:38 +0900 Subject: [PATCH 40/67] refactor unseparated_literal_suffix --- .../src/misc_early/mixed_case_hex_literals.rs | 7 ++++- clippy_lints/src/misc_early/mod.rs | 28 ++++--------------- .../misc_early/unseparated_literal_suffix.rs | 9 +++--- 3 files changed, 15 insertions(+), 29 deletions(-) diff --git a/clippy_lints/src/misc_early/mixed_case_hex_literals.rs b/clippy_lints/src/misc_early/mixed_case_hex_literals.rs index fbcbfa2556fea..98b0f7b56644b 100644 --- a/clippy_lints/src/misc_early/mixed_case_hex_literals.rs +++ b/clippy_lints/src/misc_early/mixed_case_hex_literals.rs @@ -4,7 +4,12 @@ use rustc_lint::EarlyContext; use super::MIXED_CASE_HEX_LITERALS; -pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, maybe_last_sep_idx: usize, lit_snip: String) { +pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, suffix: &str, lit_snip: String) { + let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) { + val + } else { + return; // It's useless so shouldn't lint. + }; if maybe_last_sep_idx <= 2 { // It's meaningless or causes range error. return; diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index 4b1d55beca8f5..146d0c367a853 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -7,12 +7,11 @@ mod unneeded_wildcard_pattern; mod unseparated_literal_suffix; mod zero_prefixed_literal; -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint; use clippy_utils::source::snippet_opt; use rustc_ast::ast::{Expr, Generics, Lit, LitFloatType, LitIntType, LitKind, NodeId, Pat, PatKind}; use rustc_ast::visit::FnKind; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -333,34 +332,17 @@ impl MiscEarlyLints { LitIntType::Unsigned(ty) => ty.name_str(), LitIntType::Unsuffixed => "", }; - - let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) { - val - } else { - return; // It's useless so shouldn't lint. - }; - // Do not lint when literal is unsuffixed. - if !suffix.is_empty() && lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' { - span_lint_and_sugg( - cx, - UNSEPARATED_LITERAL_SUFFIX, - lit.span, - "integer type suffix should be separated by an underscore", - "add an underscore", - format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix), - Applicability::MachineApplicable, - ); - } - + unseparated_literal_suffix::check(cx, lit, &lit_snip, suffix, "integer"); if lit_snip.starts_with("0x") { - mixed_case_hex_literals::check(cx, lit, maybe_last_sep_idx, lit_snip) + 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 { - unseparated_literal_suffix::check(cx, lit, float_ty, lit_snip) + let suffix = float_ty.name_str(); + unseparated_literal_suffix::check(cx, lit, &lit_snip, suffix, "float") } } } diff --git a/clippy_lints/src/misc_early/unseparated_literal_suffix.rs b/clippy_lints/src/misc_early/unseparated_literal_suffix.rs index ffdd5d93a38c6..1ffdd4cf67621 100644 --- a/clippy_lints/src/misc_early/unseparated_literal_suffix.rs +++ b/clippy_lints/src/misc_early/unseparated_literal_suffix.rs @@ -1,24 +1,23 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use rustc_ast::ast::FloatTy; use rustc_ast::ast::Lit; use rustc_errors::Applicability; use rustc_lint::EarlyContext; use super::UNSEPARATED_LITERAL_SUFFIX; -pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, float_ty: FloatTy, lit_snip: String) { - let suffix = float_ty.name_str(); +pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &String, 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. }; - if lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' { + // Do not lint when literal is unsuffixed. + if !suffix.is_empty() && lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' { span_lint_and_sugg( cx, UNSEPARATED_LITERAL_SUFFIX, lit.span, - "float type suffix should be separated by an underscore", + &format!("{} type suffix should be separated by an underscore", sugg_type), "add an underscore", format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix), Applicability::MachineApplicable, From 3fbb060379485605fc8649ba5e749b141fbb9335 Mon Sep 17 00:00:00 2001 From: Takayuki Date: Wed, 5 May 2021 06:53:04 +0900 Subject: [PATCH 41/67] replace lit_snip type with &str --- clippy_lints/src/misc_early/mixed_case_hex_literals.rs | 2 +- clippy_lints/src/misc_early/mod.rs | 4 ++-- clippy_lints/src/misc_early/unseparated_literal_suffix.rs | 2 +- clippy_lints/src/misc_early/zero_prefixed_literal.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/misc_early/mixed_case_hex_literals.rs b/clippy_lints/src/misc_early/mixed_case_hex_literals.rs index 98b0f7b56644b..80e2421310078 100644 --- a/clippy_lints/src/misc_early/mixed_case_hex_literals.rs +++ b/clippy_lints/src/misc_early/mixed_case_hex_literals.rs @@ -4,7 +4,7 @@ use rustc_lint::EarlyContext; use super::MIXED_CASE_HEX_LITERALS; -pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, suffix: &str, lit_snip: String) { +pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, suffix: &str, lit_snip: &str) { let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) { val } else { diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index 146d0c367a853..dd38316fa25b0 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -334,11 +334,11 @@ impl MiscEarlyLints { }; unseparated_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) + 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) + 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(); diff --git a/clippy_lints/src/misc_early/unseparated_literal_suffix.rs b/clippy_lints/src/misc_early/unseparated_literal_suffix.rs index 1ffdd4cf67621..2018aa6184a8d 100644 --- a/clippy_lints/src/misc_early/unseparated_literal_suffix.rs +++ b/clippy_lints/src/misc_early/unseparated_literal_suffix.rs @@ -5,7 +5,7 @@ use rustc_lint::EarlyContext; use super::UNSEPARATED_LITERAL_SUFFIX; -pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &String, suffix: &str, sugg_type: &str) { +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 { diff --git a/clippy_lints/src/misc_early/zero_prefixed_literal.rs b/clippy_lints/src/misc_early/zero_prefixed_literal.rs index 16add0fa1b43b..4963bba82f2da 100644 --- a/clippy_lints/src/misc_early/zero_prefixed_literal.rs +++ b/clippy_lints/src/misc_early/zero_prefixed_literal.rs @@ -5,7 +5,7 @@ use rustc_lint::EarlyContext; use super::ZERO_PREFIXED_LITERAL; -pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: String) { +pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str) { span_lint_and_then( cx, ZERO_PREFIXED_LITERAL, From 83329ec7050da5093b0310ba3140c8c5ec093321 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 4 May 2021 17:20:15 -0500 Subject: [PATCH 42/67] Fix unused_unit macro false positive --- clippy_lints/src/unused_unit.rs | 4 +++- tests/ui/unused_unit.fixed | 7 +++++++ tests/ui/unused_unit.rs | 7 +++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index ce2d0b3ab2f24..e14945651f501 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -47,7 +47,9 @@ impl EarlyLintPass for UnusedUnit { if_chain! { if let Some(stmt) = block.stmts.last(); if let ast::StmtKind::Expr(ref expr) = stmt.kind; - if is_unit_expr(expr) && !stmt.span.from_expansion(); + if is_unit_expr(expr); + let ctxt = block.span.ctxt(); + if stmt.span.ctxt() == ctxt && expr.span.ctxt() == ctxt; then { let sp = expr.span; span_lint_and_sugg( diff --git a/tests/ui/unused_unit.fixed b/tests/ui/unused_unit.fixed index a192ebde3ebf4..7bb43cf7ae82d 100644 --- a/tests/ui/unused_unit.fixed +++ b/tests/ui/unused_unit.fixed @@ -80,3 +80,10 @@ fn test2(){} #[rustfmt::skip] fn test3(){} + +fn macro_expr() { + macro_rules! e { + () => (()); + } + e!() +} diff --git a/tests/ui/unused_unit.rs b/tests/ui/unused_unit.rs index 96041a7dd850e..21073fb802ada 100644 --- a/tests/ui/unused_unit.rs +++ b/tests/ui/unused_unit.rs @@ -80,3 +80,10 @@ fn test2() ->(){} #[rustfmt::skip] fn test3()-> (){} + +fn macro_expr() { + macro_rules! e { + () => (()); + } + e!() +} From f79a2a3990e88cba2a2d4c55828a97d2293d6992 Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Wed, 5 May 2021 04:52:03 -0700 Subject: [PATCH 43/67] needless_collect: use `node_type_opt` instead of `node_type` It may prevent future ICEs. --- clippy_lints/src/loops/needless_collect.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index 88d586c934635..9662a0b22a3ab 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -27,7 +27,7 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont if chain_method.ident.name == sym!(collect) && is_trait_method(cx, &args[0], sym::Iterator); if let Some(generic_args) = chain_method.args; if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0); - let ty = cx.typeck_results().node_type(ty.hir_id); + if let Some(ty) = cx.typeck_results().node_type_opt(ty.hir_id); if is_type_diagnostic_item(cx, ty, sym::vec_type) || is_type_diagnostic_item(cx, ty, sym::vecdeque_type) || match_type(cx, ty, &paths::BTREEMAP) @@ -82,7 +82,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo if let ExprKind::MethodCall(method_name, collect_span, &[ref iter_source], ..) = init_expr.kind; if method_name.ident.name == sym!(collect) && is_trait_method(cx, init_expr, sym::Iterator); if let Some(hir_id) = get_hir_id(*ty, method_name.args); - if let ty = cx.typeck_results().node_type(hir_id); + if let Some(ty) = cx.typeck_results().node_type_opt(hir_id); if is_type_diagnostic_item(cx, ty, sym::vec_type) || is_type_diagnostic_item(cx, ty, sym::vecdeque_type) || is_type_diagnostic_item(cx, ty, sym::BinaryHeap) || From 344f04bea6fd61a10d079ca51d331307f4747288 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 5 May 2021 08:57:09 -0500 Subject: [PATCH 44/67] Fix stack overflow in `redundant_pattern_matching` --- clippy_lints/src/matches.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index c7a25095bf6fa..a70e8b26087ef 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1712,6 +1712,7 @@ mod redundant_pattern_match { 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::{ @@ -1739,6 +1740,13 @@ mod redundant_pattern_match { /// 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>) -> bool { + if !seen.insert(ty) { + return false; + } if !ty.needs_drop(cx.tcx, cx.param_env) { false } else if !cx @@ -1750,12 +1758,12 @@ mod redundant_pattern_match { // 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(cx, ty)), - ty::Array(ty, _) => type_needs_ordered_drop(cx, ty), + 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(cx, ty)), + .any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)), _ => true, } } @@ -1772,7 +1780,7 @@ mod redundant_pattern_match { { // Check all of the generic arguments. if let ty::Adt(_, subs) = ty.kind() { - subs.types().any(|ty| type_needs_ordered_drop(cx, ty)) + subs.types().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)) } else { true } From 59874f3bea8fc27ec0659768b661c7961e73d4ab Mon Sep 17 00:00:00 2001 From: flip1995 Date: Wed, 5 May 2021 15:35:14 +0200 Subject: [PATCH 45/67] Add regression test for stack overflow in redundant_pattern_matching --- tests/ui/crashes/ice-7169.rs | 9 +++++++++ tests/ui/crashes/ice-7169.stderr | 10 ++++++++++ 2 files changed, 19 insertions(+) create mode 100644 tests/ui/crashes/ice-7169.rs create mode 100644 tests/ui/crashes/ice-7169.stderr diff --git a/tests/ui/crashes/ice-7169.rs b/tests/ui/crashes/ice-7169.rs new file mode 100644 index 0000000000000..82095febc194a --- /dev/null +++ b/tests/ui/crashes/ice-7169.rs @@ -0,0 +1,9 @@ +#[derive(Default)] +struct A { + a: Vec>, + b: T, +} + +fn main() { + if let Ok(_) = Ok::<_, ()>(A::::default()) {} +} diff --git a/tests/ui/crashes/ice-7169.stderr b/tests/ui/crashes/ice-7169.stderr new file mode 100644 index 0000000000000..5a9cd32380a19 --- /dev/null +++ b/tests/ui/crashes/ice-7169.stderr @@ -0,0 +1,10 @@ +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/ice-7169.rs:8:12 + | +LL | if let Ok(_) = Ok::<_, ()>(A::::default()) {} + | -------^^^^^-------------------------------------- help: try this: `if Ok::<_, ()>(A::::default()).is_ok()` + | + = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` + +error: aborting due to previous error + From 637751ff62662247480ae1641042a019d8e6609a Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sun, 31 Jan 2021 14:46:09 +0100 Subject: [PATCH 46/67] Metadata collection lint: Basic lint collection WIP-2021-02-01 WIP-2021-02-01 WIP-2021-02-13 --- .gitignore | 1 + Cargo.toml | 1 + clippy_lints/Cargo.toml | 2 + clippy_lints/src/lib.rs | 3 + clippy_lints/src/utils/internal_lints.rs | 3 + .../internal_lints/metadata_collector.rs | 206 ++++++++++++++++++ clippy_lints/src/utils/mod.rs | 2 +- tests/dogfood.rs | 9 + 8 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/utils/internal_lints/metadata_collector.rs diff --git a/.gitignore b/.gitignore index 376528e30853a..523bab1882836 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ out # gh pages docs util/gh-pages/lints.json +**/metadata_collection.json # rustfmt backups *.rs.bk diff --git a/Cargo.toml b/Cargo.toml index cade44a0a9ab4..bdee8e408213f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,6 +52,7 @@ rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util" } deny-warnings = [] integration = ["tempfile"] internal-lints = ["clippy_lints/internal-lints"] +metadata-collector-lint = ["clippy_lints/metadata-collector-lint"] [package.metadata.rust-analyzer] # This package uses #[feature(rustc_private)] diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 05cdd9d064a8e..d1d56fc84f948 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -20,6 +20,7 @@ pulldown-cmark = { version = "0.8", default-features = false } quine-mc_cluskey = "0.2.2" regex-syntax = "0.6" serde = { version = "1.0", features = ["derive"] } +serde_json = { version = "1.0", optional = true } toml = "0.5.3" unicode-normalization = "0.1" semver = "0.11" @@ -32,6 +33,7 @@ url = { version = "2.1.0", features = ["serde"] } deny-warnings = [] # build clippy with internal lints enabled, off by default internal-lints = ["clippy_utils/internal-lints"] +metadata-collector-lint = ["serde_json"] [package.metadata.rust-analyzer] # This crate uses #[feature(rustc_private)] diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 40a793e48cf0d..7668d4dd0b105 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1004,6 +1004,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass); } + #[cfg(feature = "metadata-collector-lint")] + store.register_late_pass(|| box utils::internal_lints::metadata_collector::MetadataCollector::default()); + store.register_late_pass(|| box utils::author::Author); store.register_late_pass(|| box await_holding_invalid::AwaitHolding); store.register_late_pass(|| box serde_api::SerdeApi); diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 3d3d0e19d2622..ee7be24eae801 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -32,6 +32,9 @@ use rustc_typeck::hir_ty_to_ty; use std::borrow::{Borrow, Cow}; +#[cfg(feature = "metadata-collector-lint")] +pub mod metadata_collector; + declare_clippy_lint! { /// **What it does:** Checks for various things we like to keep tidy in clippy. /// diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs new file mode 100644 index 0000000000000..dc4267697cba7 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -0,0 +1,206 @@ +//! 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 +//! `internal_metadata_lint` +//! +//! The metadata currently contains: +//! - [ ] TODO The lint declaration line for [#1303](https://github.com/rust-lang/rust-clippy/issues/1303) +//! and [#6492](https://github.com/rust-lang/rust-clippy/issues/6492) +//! - [ ] TODO The Applicability for each lint for [#4310](https://github.com/rust-lang/rust-clippy/issues/4310) + +// # Applicability +// - TODO xFrednet 2021-01-17: Find all methods that take and modify applicability or predefine +// them? +// - TODO xFrednet 2021-01-17: Find lint emit and collect applicability +// # NITs +// - TODO xFrednet 2021-02-13: Collect depreciations and maybe renames + +use if_chain::if_chain; +use rustc_hir::{ExprKind, Item, ItemKind, Mutability}; +use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{sym, Loc, Span}; +use serde::Serialize; +use std::fs::OpenOptions; +use std::io::prelude::*; + +use crate::utils::internal_lints::is_lint_ref_type; +use crate::utils::span_lint; + +const OUTPUT_FILE: &str = "metadata_collection.json"; +const BLACK_LISTED_LINTS: [&str; 2] = ["lint_author", "deep_code_inspection"]; + +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:** Collects metadata about clippy lints for the website. [...] " + /// } + /// ``` + pub INTERNAL_METADATA_COLLECTOR, + internal, + "A busy bee collection metadata about lints" +} + +impl_lint_pass!(MetadataCollector => [INTERNAL_METADATA_COLLECTOR]); + +#[allow(clippy::module_name_repetitions)] +#[derive(Debug, Clone, Default)] +pub struct MetadataCollector { + lints: Vec, +} + +impl Drop for MetadataCollector { + fn drop(&mut self) { + // You might ask: How hacky is this? + // My answer: YES + let mut file = OpenOptions::new().write(true).create(true).open(OUTPUT_FILE).unwrap(); + writeln!(file, "{}", serde_json::to_string_pretty(&self.lints).unwrap()).unwrap(); + } +} + +#[derive(Debug, Clone, Serialize)] +struct LintMetadata { + id: String, + id_span: SerializableSpan, + group: String, + docs: String, +} + +#[derive(Debug, Clone, Serialize)] +struct SerializableSpan { + path: String, + line: usize, +} + +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), + line: 1, + } + } +} + +impl<'tcx> LateLintPass<'tcx> for MetadataCollector { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if_chain! { + if let ItemKind::Static(ref ty, Mutability::Not, body_id) = item.kind; + if is_lint_ref_type(cx, ty); + let expr = &cx.tcx.hir().body(body_id).value; + if let ExprKind::AddrOf(_, _, ref inner_exp) = expr.kind; + if let ExprKind::Struct(_, _, _) = inner_exp.kind; + then { + let lint_name = item.ident.name.as_str().to_string().to_ascii_lowercase(); + if BLACK_LISTED_LINTS.contains(&lint_name.as_str()) { + return; + } + + let group: String; + let result = cx.lint_store.check_lint_name(lint_name.as_str(), Some(sym::clippy)); + if let CheckLintNameResult::Tool(Ok(lint_lst)) = result { + if let Some(group_some) = get_lint_group(cx, lint_lst[0]) { + group = group_some; + } else { + lint_collection_error(cx, item, "Unable to determine lint group"); + return; + } + } else { + lint_collection_error(cx, item, "Unable to find lint in lint_store"); + return; + } + + let docs: String; + if let Some(docs_some) = extract_attr_docs(item) { + docs = docs_some; + } else { + lint_collection_error(cx, item, "could not collect the lint documentation"); + return; + }; + + self.lints.push(LintMetadata { + id: lint_name, + id_span: SerializableSpan::from_item(cx, item), + group, + docs, + }); + } + } + } +} + +/// 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` +fn extract_attr_docs(item: &Item<'_>) -> Option { + item.attrs + .iter() + .filter_map(|ref x| x.doc_str()) + .fold(None, |acc, sym| { + let mut doc_str = sym.as_str().to_string(); + doc_str.push('\n'); + + #[allow(clippy::option_if_let_else)] // See clippy#6737 + if let Some(mut x) = acc { + x.push_str(&doc_str); + Some(x) + } else { + Some(doc_str) + } + + // acc.map_or(Some(doc_str), |mut x| { + // x.push_str(&doc_str); + // Some(x) + // }) + }) +} + +fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option { + for (group_name, lints, _) in &cx.lint_store.get_lint_groups() { + if lints.iter().any(|x| *x == lint_id) { + return Some((*group_name).to_string()); + } + } + + None +} + +fn lint_collection_error(cx: &LateContext<'_>, item: &Item<'_>, message: &str) { + span_lint( + cx, + INTERNAL_METADATA_COLLECTOR, + item.ident.span, + &format!("Metadata collection error for `{}`: {}", item.ident.name, message), + ); +} diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index d8b31344e6d8b..b67448e3a5740 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1,5 +1,5 @@ pub mod author; pub mod conf; pub mod inspector; -#[cfg(feature = "internal-lints")] +#[cfg(any(feature = "internal-lints", feature = "metadata-collector-lint"))] pub mod internal_lints; diff --git a/tests/dogfood.rs b/tests/dogfood.rs index d92530f073f56..b0da0bfcc3576 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -1,3 +1,8 @@ +//! This test is a part of quality control and makes clippy eat what it produces. Awesome lints and +//! long error messages +//! +//! See [Eating your own dog food](https://en.wikipedia.org/wiki/Eating_your_own_dog_food) for context + // Dogfood cannot run on Windows #![cfg(not(windows))] #![feature(once_cell)] @@ -36,6 +41,10 @@ fn dogfood_clippy() { command.args(&["-D", "clippy::internal"]); } + if cfg!(feature = "metadata-collector-lint") { + command.args(&["-D", "clippy::internal"]); + } + let output = command.output().unwrap(); println!("status: {}", output.status); From 060e0e9f9382cb7975165cede8d04b7bbe26bcb2 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sun, 14 Feb 2021 16:17:07 +0100 Subject: [PATCH 47/67] Metadata collection lint: Basic applicability collection --- .../internal_lints/metadata_collector.rs | 178 ++++++++++++++---- clippy_utils/src/paths.rs | 2 + 2 files changed, 147 insertions(+), 33 deletions(-) diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index dc4267697cba7..677f9fcc83f2d 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -2,10 +2,14 @@ //! 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 -//! `internal_metadata_lint` +//! `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) //! //! The metadata currently contains: -//! - [ ] TODO The lint declaration line for [#1303](https://github.com/rust-lang/rust-clippy/issues/1303) +//! - [x] TODO The lint declaration line for [#1303](https://github.com/rust-lang/rust-clippy/issues/1303) //! and [#6492](https://github.com/rust-lang/rust-clippy/issues/6492) //! - [ ] TODO The Applicability for each lint for [#4310](https://github.com/rust-lang/rust-clippy/issues/4310) @@ -17,20 +21,34 @@ // - TODO xFrednet 2021-02-13: Collect depreciations and maybe renames use if_chain::if_chain; -use rustc_hir::{ExprKind, Item, ItemKind, Mutability}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_hir::{self as hir, ExprKind, Item, ItemKind, Mutability}; use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{sym, Loc, Span}; +use rustc_span::{sym, Loc, Span, Symbol}; use serde::Serialize; use std::fs::OpenOptions; use std::io::prelude::*; use crate::utils::internal_lints::is_lint_ref_type; -use crate::utils::span_lint; +use crate::utils::{last_path_segment, match_function_call, match_type, paths, span_lint, walk_ptrs_ty_depth}; +/// This is the output file of the lint collector. const OUTPUT_FILE: &str = "metadata_collection.json"; +/// These lints are excluded from the export. const BLACK_LISTED_LINTS: [&str; 2] = ["lint_author", "deep_code_inspection"]; +// TODO xFrednet 2021-02-15: `span_lint_and_then` & `span_lint_hir_and_then` requires special +// handling +#[rustfmt::skip] +const LINT_EMISSION_FUNCTIONS: [&[&str]; 5] = [ + &["clippy_lints", "utils", "diagnostics", "span_lint"], + &["clippy_lints", "utils", "diagnostics", "span_lint_and_help"], + &["clippy_lints", "utils", "diagnostics", "span_lint_and_note"], + &["clippy_lints", "utils", "diagnostics", "span_lint_hir"], + &["clippy_lints", "utils", "diagnostics", "span_lint_and_sugg"], +]; + declare_clippy_lint! { /// **What it does:** Collects metadata about clippy lints for the website. /// @@ -66,12 +84,21 @@ impl_lint_pass!(MetadataCollector => [INTERNAL_METADATA_COLLECTOR]); #[derive(Debug, Clone, Default)] pub struct MetadataCollector { lints: Vec, + applicability_into: FxHashMap, } impl Drop for MetadataCollector { + /// You might ask: How hacky is this? + /// My answer: YES fn drop(&mut self) { - // You might ask: How hacky is this? - // My answer: YES + let mut applicability_info = std::mem::take(&mut self.applicability_into); + + // Mapping the final data + self.lints + .iter_mut() + .for_each(|x| x.applicability = applicability_info.remove(&x.id)); + + // Outputting let mut file = OpenOptions::new().write(true).create(true).open(OUTPUT_FILE).unwrap(); writeln!(file, "{}", serde_json::to_string_pretty(&self.lints).unwrap()).unwrap(); } @@ -83,6 +110,21 @@ struct LintMetadata { id_span: SerializableSpan, group: String, docs: String, + /// This field is only used in the output and will only be + /// mapped shortly before the actual output. + applicability: Option, +} + +impl LintMetadata { + fn new(id: String, id_span: SerializableSpan, group: String, docs: String) -> Self { + Self { + id, + id_span, + group, + docs, + applicability: None, + } + } } #[derive(Debug, Clone, Serialize)] @@ -101,12 +143,31 @@ impl SerializableSpan { Self { path: format!("{}", loc.file.name), - line: 1, + line: loc.line, } } } +#[derive(Debug, Clone, Default, Serialize)] +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. + has_multi_suggestion: bool, + /// These are all the available applicability values for the lint suggestions + applicabilities: FxHashSet, +} + impl<'tcx> LateLintPass<'tcx> 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<'tcx>, item: &'tcx Item<'_>) { if_chain! { if let ItemKind::Static(ref ty, Mutability::Not, body_id) = item.kind; @@ -115,7 +176,7 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector { if let ExprKind::AddrOf(_, _, ref inner_exp) = expr.kind; if let ExprKind::Struct(_, _, _) = inner_exp.kind; then { - let lint_name = item.ident.name.as_str().to_string().to_ascii_lowercase(); + let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase(); if BLACK_LISTED_LINTS.contains(&lint_name.as_str()) { return; } @@ -126,11 +187,11 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector { if let Some(group_some) = get_lint_group(cx, lint_lst[0]) { group = group_some; } else { - lint_collection_error(cx, item, "Unable to determine lint group"); + lint_collection_error_item(cx, item, "Unable to determine lint group"); return; } } else { - lint_collection_error(cx, item, "Unable to find lint in lint_store"); + lint_collection_error_item(cx, item, "Unable to find lint in lint_store"); return; } @@ -138,21 +199,38 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector { if let Some(docs_some) = extract_attr_docs(item) { docs = docs_some; } else { - lint_collection_error(cx, item, "could not collect the lint documentation"); + lint_collection_error_item(cx, item, "could not collect the lint documentation"); return; }; - self.lints.push(LintMetadata { - id: lint_name, - id_span: SerializableSpan::from_item(cx, item), + self.lints.push(LintMetadata::new( + lint_name, + SerializableSpan::from_item(cx, item), group, docs, + )); + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if let Some(args) = match_simple_lint_emission(cx, expr) { + if let Some((lint_name, mut applicability)) = extract_emission_info(cx, args) { + let app_info = self.applicability_into.entry(lint_name).or_default(); + applicability.drain(..).for_each(|x| { + app_info.applicabilities.insert(x); }); + } else { + lint_collection_error_span(cx, expr.span, "I found this but I can't get the lint or applicability"); } } } } +fn sym_to_string(sym: Symbol) -> String { + sym.as_str().to_string() +} + /// This function collects all documentation that has been added to an item using /// `#[doc = r""]` attributes. Several attributes are aggravated using line breaks /// @@ -166,23 +244,11 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector { fn extract_attr_docs(item: &Item<'_>) -> Option { item.attrs .iter() - .filter_map(|ref x| x.doc_str()) - .fold(None, |acc, sym| { - let mut doc_str = sym.as_str().to_string(); - doc_str.push('\n'); - - #[allow(clippy::option_if_let_else)] // See clippy#6737 - if let Some(mut x) = acc { - x.push_str(&doc_str); - Some(x) - } else { - Some(doc_str) - } - - // acc.map_or(Some(doc_str), |mut x| { - // x.push_str(&doc_str); - // Some(x) - // }) + .filter_map(|ref x| x.doc_str().map(|sym| sym.as_str().to_string())) + .reduce(|mut acc, sym| { + acc.push_str(&sym); + acc.push('\n'); + acc }) } @@ -196,7 +262,7 @@ fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option { None } -fn lint_collection_error(cx: &LateContext<'_>, item: &Item<'_>, message: &str) { +fn lint_collection_error_item(cx: &LateContext<'_>, item: &Item<'_>, message: &str) { span_lint( cx, INTERNAL_METADATA_COLLECTOR, @@ -204,3 +270,49 @@ fn lint_collection_error(cx: &LateContext<'_>, item: &Item<'_>, message: &str) { &format!("Metadata collection error for `{}`: {}", item.ident.name, message), ); } + +fn lint_collection_error_span(cx: &LateContext<'_>, span: Span, message: &str) { + span_lint( + cx, + INTERNAL_METADATA_COLLECTOR, + span, + &format!("Metadata collection error: {}", message), + ); +} + +fn match_simple_lint_emission<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, +) -> Option<&'tcx [hir::Expr<'tcx>]> { + LINT_EMISSION_FUNCTIONS + .iter() + .find_map(|emission_fn| match_function_call(cx, expr, emission_fn).map(|args| args)) +} + +/// This returns the lint name and the possible applicability of this emission +fn extract_emission_info<'tcx>(cx: &LateContext<'tcx>, args: &[hir::Expr<'_>]) -> Option<(String, Vec)> { + let mut lint_name = None; + let mut applicability = None; + + 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 + if let ExprKind::Path(ref lint_path) = arg.kind { + lint_name = Some(last_path_segment(lint_path).ident.name) + } + } else if match_type(cx, arg_ty, &paths::APPLICABILITY) { + if let ExprKind::Path(ref path) = arg.kind { + applicability = Some(last_path_segment(path).ident.name) + } + } + } + + lint_name.map(|lint_name| { + ( + sym_to_string(lint_name).to_ascii_lowercase(), + applicability.map(sym_to_string).map_or_else(Vec::new, |x| vec![x]), + ) + }) +} diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 5e6733a300f2c..91e5cd6c0465e 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -5,6 +5,8 @@ //! See for more information. pub const ANY_TRAIT: [&str; 3] = ["core", "any", "Any"]; +#[cfg(feature = "metadata-collector-lint")] +pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"]; pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"]; pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"]; pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"]; From 68d702f88ded863b9cde999f1850b32c6531fe11 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Thu, 18 Feb 2021 00:20:19 +0100 Subject: [PATCH 48/67] Metadata collection lint: Start Applicability value tracking --- .../internal_lints/metadata_collector.rs | 158 +++++++++++++++++- 1 file changed, 155 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 677f9fcc83f2d..2bdb92376cad9 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -24,14 +24,21 @@ use if_chain::if_chain; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::{self as hir, ExprKind, Item, ItemKind, Mutability}; use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId}; +use rustc_middle::ty::BorrowKind; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Loc, Span, Symbol}; +use rustc_trait_selection::infer::TyCtxtInferExt; +use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceWithHirId}; +use rustc_typeck::hir_ty_to_ty; use serde::Serialize; -use std::fs::OpenOptions; +use std::fs::{self, OpenOptions}; use std::io::prelude::*; +use std::path::Path; use crate::utils::internal_lints::is_lint_ref_type; -use crate::utils::{last_path_segment, match_function_call, match_type, paths, span_lint, walk_ptrs_ty_depth}; +use crate::utils::{ + last_path_segment, match_function_call, match_type, path_to_local_id, paths, span_lint, walk_ptrs_ty_depth, +}; /// This is the output file of the lint collector. const OUTPUT_FILE: &str = "metadata_collection.json"; @@ -91,6 +98,10 @@ impl Drop for MetadataCollector { /// You might ask: How hacky is this? /// My answer: YES fn drop(&mut self) { + if self.lints.is_empty() { + return; + } + let mut applicability_info = std::mem::take(&mut self.applicability_into); // Mapping the final data @@ -99,6 +110,9 @@ impl Drop for MetadataCollector { .for_each(|x| x.applicability = applicability_info.remove(&x.id)); // 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(&self.lints).unwrap()).unwrap(); } @@ -158,6 +172,17 @@ struct ApplicabilityInfo { applicabilities: FxHashSet, } +fn log_to_file(msg: &str) { + let mut file = OpenOptions::new() + .write(true) + .append(true) + .create(true) + .open("metadata-lint.log") + .unwrap(); + + write!(file, "{}", msg).unwrap(); +} + impl<'tcx> LateLintPass<'tcx> for MetadataCollector { /// Collecting lint declarations like: /// ```rust, ignore @@ -213,6 +238,20 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector { } } + /// 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<'tcx>, expr: &'tcx hir::Expr<'_>) { if let Some(args) = match_simple_lint_emission(cx, expr) { if let Some((lint_name, mut applicability)) = extract_emission_info(cx, args) { @@ -225,6 +264,73 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector { } } } + + /// Tracking and hopefully collecting dynamic applicability values + /// + /// Example: + /// ```rust, ignore + /// // vvv Applicability value to track + /// let mut applicability = Applicability::MachineApplicable; + /// // vvv Value Mutation + /// let suggestion = snippet_with_applicability(cx, expr.span, "_", &mut applicability); + /// // vvv Emission to link the value to the lint + /// span_lint_and_sugg( + /// cx, + /// SOME_LINT, + /// expr.span, + /// "This can be improved", + /// "try", + /// suggestion, + /// applicability, + /// ); + /// ``` + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx hir::Local<'tcx>) { + if let Some(tc) = cx.maybe_typeck_results() { + // TODO xFrednet 2021-02-14: support nested applicability (only in tuples) + let local_ty = if let Some(ty) = local.ty { + hir_ty_to_ty(cx.tcx, ty) + } else if let Some(init) = local.init { + tc.expr_ty(init) + } else { + return; + }; + + if_chain! { + if match_type(cx, local_ty, &paths::APPLICABILITY); + if let Some(body) = get_parent_body(cx, local.hir_id); + then { + let span = SerializableSpan::from_span(cx, local.span); + let local_str = crate::utils::snippet(cx, local.span, "_"); + let value_life = format!("{} -- {}:{}\n", local_str, span.path.rsplit('/').next().unwrap_or_default(), span.line); + let value_hir_id = local.pat.hir_id; + let mut tracker = ValueTracker {cx, value_hir_id, value_life}; + + cx.tcx.infer_ctxt().enter(|infcx| { + let body_owner_id = cx.tcx.hir().body_owner_def_id(body.id()); + ExprUseVisitor::new( + &mut tracker, + &infcx, + body_owner_id, + cx.param_env, + cx.typeck_results() + ) + .consume_body(body); + }); + + log_to_file(&tracker.value_life); + lint_collection_error_span(cx, local.span, "Applicability value found"); + } + } + } + } +} + +fn get_parent_body<'a, 'tcx>(cx: &'a LateContext<'tcx>, id: hir::HirId) -> Option<&'tcx hir::Body<'tcx>> { + let map = cx.tcx.hir(); + + map.parent_iter(id) + .find_map(|(parent, _)| map.maybe_body_owned_by(parent)) + .map(|body| map.body(body)) } fn sym_to_string(sym: Symbol) -> String { @@ -262,6 +368,9 @@ fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option { None } +// ================================================================== +// Lint emission +// ================================================================== fn lint_collection_error_item(cx: &LateContext<'_>, item: &Item<'_>, message: &str) { span_lint( cx, @@ -280,13 +389,16 @@ fn lint_collection_error_span(cx: &LateContext<'_>, span: Span, message: &str) { ); } +// ================================================================== +// Applicability +// ================================================================== fn match_simple_lint_emission<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, ) -> Option<&'tcx [hir::Expr<'tcx>]> { LINT_EMISSION_FUNCTIONS .iter() - .find_map(|emission_fn| match_function_call(cx, expr, emission_fn).map(|args| args)) + .find_map(|emission_fn| match_function_call(cx, expr, emission_fn)) } /// This returns the lint name and the possible applicability of this emission @@ -316,3 +428,43 @@ fn extract_emission_info<'tcx>(cx: &LateContext<'tcx>, args: &[hir::Expr<'_>]) - ) }) } + +struct ValueTracker<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + value_hir_id: hir::HirId, + value_life: String, +} + +impl<'a, 'tcx> ValueTracker<'a, 'tcx> { + fn is_value_expr(&self, expr_id: hir::HirId) -> bool { + match self.cx.tcx.hir().find(expr_id) { + Some(hir::Node::Expr(expr)) => path_to_local_id(expr, self.value_hir_id), + _ => false, + } + } +} + +impl<'a, 'tcx> Delegate<'tcx> for ValueTracker<'a, 'tcx> { + fn consume(&mut self, _place_with_id: &PlaceWithHirId<'tcx>, expr_id: hir::HirId, _: ConsumeMode) { + if self.is_value_expr(expr_id) { + // TODO xFrednet 2021-02-17: Check if lint emission and extract lint ID + todo!(); + } + } + + fn borrow(&mut self, _place_with_id: &PlaceWithHirId<'tcx>, expr_id: hir::HirId, bk: BorrowKind) { + if self.is_value_expr(expr_id) { + if let BorrowKind::MutBorrow = bk { + // TODO xFrednet 2021-02-17: Save the function + todo!(); + } + } + } + + fn mutate(&mut self, _assignee_place: &PlaceWithHirId<'tcx>, expr_id: hir::HirId) { + if self.is_value_expr(expr_id) { + // TODO xFrednet 2021-02-17: Save the new value as a mutation + todo!(); + } + } +} From 8dca1b8f618fdec3f1b354d9bafa01b32ecc8ab6 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Thu, 18 Feb 2021 23:40:33 +0100 Subject: [PATCH 49/67] Metadata collection: Collecting Applicability assign values --- .../internal_lints/metadata_collector.rs | 249 ++++++++++++++---- clippy_utils/src/paths.rs | 7 + .../track_applicability_value.rs | 46 ++++ 3 files changed, 248 insertions(+), 54 deletions(-) create mode 100644 tests/ui-internal/metadata-collector/track_applicability_value.rs diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 2bdb92376cad9..e22aa285f1756 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -24,7 +24,7 @@ use if_chain::if_chain; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::{self as hir, ExprKind, Item, ItemKind, Mutability}; use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId}; -use rustc_middle::ty::BorrowKind; +use rustc_middle::ty::{BorrowKind, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Loc, Span, Symbol}; use rustc_trait_selection::infer::TyCtxtInferExt; @@ -37,7 +37,8 @@ use std::path::Path; use crate::utils::internal_lints::is_lint_ref_type; use crate::utils::{ - last_path_segment, match_function_call, match_type, path_to_local_id, paths, span_lint, walk_ptrs_ty_depth, + get_enclosing_body, get_parent_expr_for_hir, last_path_segment, match_function_call, match_qpath, match_type, + path_to_local_id, paths, span_lint, walk_ptrs_ty_depth, }; /// This is the output file of the lint collector. @@ -147,6 +148,12 @@ struct SerializableSpan { 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) @@ -285,52 +292,54 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector { /// ); /// ``` fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx hir::Local<'tcx>) { - if let Some(tc) = cx.maybe_typeck_results() { - // TODO xFrednet 2021-02-14: support nested applicability (only in tuples) - let local_ty = if let Some(ty) = local.ty { - hir_ty_to_ty(cx.tcx, ty) - } else if let Some(init) = local.init { - tc.expr_ty(init) - } else { - return; - }; - - if_chain! { - if match_type(cx, local_ty, &paths::APPLICABILITY); - if let Some(body) = get_parent_body(cx, local.hir_id); - then { - let span = SerializableSpan::from_span(cx, local.span); - let local_str = crate::utils::snippet(cx, local.span, "_"); - let value_life = format!("{} -- {}:{}\n", local_str, span.path.rsplit('/').next().unwrap_or_default(), span.line); - let value_hir_id = local.pat.hir_id; - let mut tracker = ValueTracker {cx, value_hir_id, value_life}; - - cx.tcx.infer_ctxt().enter(|infcx| { - let body_owner_id = cx.tcx.hir().body_owner_def_id(body.id()); - ExprUseVisitor::new( - &mut tracker, - &infcx, - body_owner_id, - cx.param_env, - cx.typeck_results() - ) - .consume_body(body); - }); - - log_to_file(&tracker.value_life); - lint_collection_error_span(cx, local.span, "Applicability value found"); + if_chain! { + if let Some(local_ty) = get_local_type(cx, local); + if match_type(cx, local_ty, &paths::APPLICABILITY); + if let Some(body) = get_enclosing_body(cx, local.hir_id); + then { + // TODO xFrednet: 2021-02-19: Remove debug code + let span = SerializableSpan::from_span(cx, local.span); + let local_str = crate::utils::snippet(cx, local.span, "_"); + log_to_file(&format!("{} -- {}\n", local_str, span)); + + let value_hir_id = local.pat.hir_id; + let mut tracker = ValueTracker::new(cx, value_hir_id); + if let Some(init_expr) = local.init { + tracker.process_assign_expr(init_expr) } + + // TODO xFrednet 2021-02-18: Support nested bodies + // Note: The `ExprUseVisitor` only searches though one body, this means that values + // references in nested bodies like closures are not found by this simple visitor. + cx.tcx.infer_ctxt().enter(|infcx| { + let body_owner_id = cx.tcx.hir().body_owner_def_id(body.id()); + ExprUseVisitor::new( + &mut tracker, + &infcx, + body_owner_id, + cx.param_env, + cx.typeck_results() + ) + .consume_body(body); + }); + + log_to_file(&format!("{:?}\n", tracker.value_mutations)); } } } } -fn get_parent_body<'a, 'tcx>(cx: &'a LateContext<'tcx>, id: hir::HirId) -> Option<&'tcx hir::Body<'tcx>> { - let map = cx.tcx.hir(); +fn get_local_type<'a>(cx: &'a LateContext<'_>, local: &'a hir::Local<'_>) -> Option> { + // TODO xFrednet 2021-02-14: support nested applicability (only in tuples) + if let Some(tc) = cx.maybe_typeck_results() { + if let Some(ty) = local.ty { + return Some(hir_ty_to_ty(cx.tcx, ty)); + } else if let Some(init) = local.init { + return Some(tc.expr_ty(init)); + } + } - map.parent_iter(id) - .find_map(|(parent, _)| map.maybe_body_owned_by(parent)) - .map(|body| map.body(body)) + None } fn sym_to_string(sym: Symbol) -> String { @@ -429,42 +438,174 @@ fn extract_emission_info<'tcx>(cx: &LateContext<'tcx>, args: &[hir::Expr<'_>]) - }) } -struct ValueTracker<'a, 'tcx> { - cx: &'a LateContext<'tcx>, +#[allow(dead_code)] +struct ValueTracker<'a, 'hir> { + cx: &'a LateContext<'hir>, value_hir_id: hir::HirId, - value_life: String, + value_mutations: Vec>, } -impl<'a, 'tcx> ValueTracker<'a, 'tcx> { +impl<'a, 'hir> ValueTracker<'a, 'hir> { + fn new(cx: &'a LateContext<'hir>, value_hir_id: hir::HirId) -> Self { + Self { + cx, + value_hir_id, + value_mutations: Vec::new(), + } + } + fn is_value_expr(&self, expr_id: hir::HirId) -> bool { match self.cx.tcx.hir().find(expr_id) { Some(hir::Node::Expr(expr)) => path_to_local_id(expr, self.value_hir_id), _ => false, } } + + /// This function extracts possible `ApplicabilityModifier` from an assign statement like this: + /// + /// ```rust, ignore + /// // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv The expression to process + /// let value = Applicability::MachineApplicable; + /// ``` + fn process_assign_expr(&mut self, expr: &'hir hir::Expr<'hir>) { + // This is a bit more complicated. I'll therefor settle on the simple solution of + // simplifying the cases we support. + match &expr.kind { + hir::ExprKind::Call(func_expr, ..) => { + // We only deal with resolved paths as this is the usual case. Other expression kinds like closures + // etc. are hard to track but might be a worthy improvement in the future + if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = func_expr.kind { + self.value_mutations.push(ApplicabilityModifier::Producer(path)); + } else { + let msg = format!( + "Unsupported Call expression at: {}", + SerializableSpan::from_span(self.cx, func_expr.span) + ); + self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); + } + }, + hir::ExprKind::MethodCall(..) => { + let msg = format!( + "Unsupported MethodCall expression at: {}", + SerializableSpan::from_span(self.cx, expr.span) + ); + self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); + }, + // We can ignore ifs without an else block because those can't be used as an assignment + hir::ExprKind::If(_con, if_block, Some(else_block)) => { + self.process_assign_expr(if_block); + self.process_assign_expr(else_block); + }, + hir::ExprKind::Match(_expr, arms, _) => { + for arm in *arms { + self.process_assign_expr(arm.body); + } + }, + hir::ExprKind::Loop(block, ..) | hir::ExprKind::Block(block, ..) => { + if let Some(block_expr) = block.expr { + self.process_assign_expr(block_expr); + } + }, + hir::ExprKind::Path(path) => { + for enum_value in &paths::APPLICABILITY_VALUES { + if match_qpath(path, enum_value) { + self.value_mutations + .push(ApplicabilityModifier::ConstValue(enum_value[2].to_string())); + } + } + }, + // hir::ExprKind::Field(expr, ident) => not supported + // hir::ExprKind::Index(expr, expr) => not supported + _ => { + let msg = format!( + "Unexpected expression at: {}", + SerializableSpan::from_span(self.cx, expr.span) + ); + self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); + }, + } + } } -impl<'a, 'tcx> Delegate<'tcx> for ValueTracker<'a, 'tcx> { - fn consume(&mut self, _place_with_id: &PlaceWithHirId<'tcx>, expr_id: hir::HirId, _: ConsumeMode) { +impl<'a, 'hir> Delegate<'hir> for ValueTracker<'a, 'hir> { + fn consume(&mut self, _place_with_id: &PlaceWithHirId<'hir>, expr_id: hir::HirId, _: ConsumeMode) { if self.is_value_expr(expr_id) { // TODO xFrednet 2021-02-17: Check if lint emission and extract lint ID - todo!(); + if let Some(hir::Node::Expr(expr)) = self.cx.tcx.hir().find(expr_id) { + let span = SerializableSpan::from_span(self.cx, expr.span); + log_to_file(&format!("- consume {}\n", span)); + } } } - fn borrow(&mut self, _place_with_id: &PlaceWithHirId<'tcx>, expr_id: hir::HirId, bk: BorrowKind) { + fn borrow(&mut self, _place_with_id: &PlaceWithHirId<'hir>, expr_id: hir::HirId, bk: BorrowKind) { if self.is_value_expr(expr_id) { if let BorrowKind::MutBorrow = bk { // TODO xFrednet 2021-02-17: Save the function - todo!(); + if let Some(hir::Node::Expr(expr)) = self.cx.tcx.hir().find(expr_id) { + let span = SerializableSpan::from_span(self.cx, expr.span); + log_to_file(&format!("- &mut {}\n", span)); + } } } } - fn mutate(&mut self, _assignee_place: &PlaceWithHirId<'tcx>, expr_id: hir::HirId) { - if self.is_value_expr(expr_id) { - // TODO xFrednet 2021-02-17: Save the new value as a mutation - todo!(); + fn mutate(&mut self, _assignee_place: &PlaceWithHirId<'hir>, expr_id: hir::HirId) { + if_chain! { + if self.is_value_expr(expr_id); + if let Some(expr) = get_parent_expr_for_hir(self.cx, expr_id); + if let hir::ExprKind::Assign(_value_expr, assign_expr, ..) = expr.kind; + then { + self.process_assign_expr(assign_expr); + } } } } + +/// The life of a value in Rust is a true adventure. These are the corner stones of such a +/// fairy tale. Let me introduce you to the possible stepping stones a value might have in +/// in our crazy word: +#[derive(Debug)] +#[allow(dead_code)] +enum ApplicabilityModifier<'hir> { + Unknown(String), + /// A simple constant value. + /// + /// This is the actual character of a value. It's baseline. This only defines where the value + /// started. As in real life it can still change and fully decide who it wants to be. + ConstValue(String), + /// A producer is a function that returns an applicability value. + /// + /// This is the heritage of this value. This value comes from a long family tree and is not + /// just a black piece of paper. The evaluation of this stepping stone needs additional + /// context. We therefore only add a reference. This reference will later be used to ask + /// the librarian about the possible initial character that this value might have. + Producer(&'hir hir::Path<'hir>), + /// A modifier that takes the given applicability and might modify it + /// + /// What would an RPG be without it's NPCs. The special thing about modifiers is that they can + /// be actively interested in the story of the value and might make decisions based on the + /// character of this hero. This means that a modifier doesn't just force its way into the life + /// of our hero but it actually asks him how he's been. The possible modification is a result + /// of the situation. + /// + /// Take this part of our heroes life very seriously! + Modifier(&'hir hir::Path<'hir>), + /// The actual emission of a lint + /// + /// All good things must come to an end. Even the life of your awesome applicability hero. He + /// was the bravest soul that has ever wondered this earth. Songs will be written about his + /// heroic deeds. Castles will be named after him and the world how we know it will never be + /// the same! + /// + /// Is this a happy ending? Did he archive what he wanted in his life? Yes, YES, he has lived a + /// life and he will continue to live in all the lint suggestions that can be applied or just + /// displayed by Clippy. He might be no more, but his legacy will serve generations to come. + LintEmit(LintEmission), +} + +#[derive(Debug)] +struct LintEmission { + lint: String, + is_multi_line_sugg: bool, +} diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 91e5cd6c0465e..46f58b788e6eb 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -7,6 +7,13 @@ pub const ANY_TRAIT: [&str; 3] = ["core", "any", "Any"]; #[cfg(feature = "metadata-collector-lint")] pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"]; +#[cfg(feature = "metadata-collector-lint")] +pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [ + ["rustc_lint_defs", "Applicability", "MachineApplicable"], + ["rustc_lint_defs", "Applicability", "MaybeIncorrect"], + ["rustc_lint_defs", "Applicability", "HasPlaceholders"], + ["rustc_lint_defs", "Applicability", "Unspecified"], +]; pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"]; pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"]; pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"]; diff --git a/tests/ui-internal/metadata-collector/track_applicability_value.rs b/tests/ui-internal/metadata-collector/track_applicability_value.rs new file mode 100644 index 0000000000000..75fae623e6109 --- /dev/null +++ b/tests/ui-internal/metadata-collector/track_applicability_value.rs @@ -0,0 +1,46 @@ +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate rustc_ast; +extern crate rustc_errors; +extern crate rustc_lint; +extern crate rustc_session; +extern crate rustc_span; + +use rustc_ast::ast::Expr; +use rustc_errors::{Applicability, DiagnosticBuilder}; +use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +fn producer_fn() -> Applicability { + Applicability::MachineApplicable +} + +fn modifier_fn(applicability: &mut Applicability) { + if let Applicability::MaybeIncorrect = applicability { + *applicability = Applicability::HasPlaceholders; + } +} + +struct Muh; + +impl Muh { + fn producer_method() -> Applicability { + Applicability::MachineApplicable + } +} + +fn main() { + let mut applicability = producer_fn(); + applicability = Applicability::MachineApplicable; + applicability = Muh::producer_method(); + + applicability = if true { + Applicability::HasPlaceholders + } else { + Applicability::MaybeIncorrect + }; + + modifier_fn(&mut applicability); +} From ee130d066d362118cd0bf756de42ee08a2bfc301 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 20 Feb 2021 00:35:28 +0100 Subject: [PATCH 50/67] Metadata collection: Tracking Applicability mut borrows --- .../internal_lints/metadata_collector.rs | 57 ++++++++++++++++--- 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index e22aa285f1756..c8637531d9428 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -38,7 +38,7 @@ use std::path::Path; use crate::utils::internal_lints::is_lint_ref_type; use crate::utils::{ get_enclosing_body, get_parent_expr_for_hir, last_path_segment, match_function_call, match_qpath, match_type, - path_to_local_id, paths, span_lint, walk_ptrs_ty_depth, + path_to_local_id, paths, span_lint, walk_ptrs_ty_depth, get_parent_expr }; /// This is the output file of the lint collector. @@ -478,7 +478,7 @@ impl<'a, 'hir> ValueTracker<'a, 'hir> { self.value_mutations.push(ApplicabilityModifier::Producer(path)); } else { let msg = format!( - "Unsupported Call expression at: {}", + "Unsupported assign Call expression at: {}", SerializableSpan::from_span(self.cx, func_expr.span) ); self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); @@ -486,7 +486,7 @@ impl<'a, 'hir> ValueTracker<'a, 'hir> { }, hir::ExprKind::MethodCall(..) => { let msg = format!( - "Unsupported MethodCall expression at: {}", + "Unsupported assign MethodCall expression at: {}", SerializableSpan::from_span(self.cx, expr.span) ); self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); @@ -518,13 +518,56 @@ impl<'a, 'hir> ValueTracker<'a, 'hir> { // hir::ExprKind::Index(expr, expr) => not supported _ => { let msg = format!( - "Unexpected expression at: {}", + "Unexpected assign expression at: {}", SerializableSpan::from_span(self.cx, expr.span) ); self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); }, } } + + fn process_borrow_expr(&mut self, access_hir_id: hir::HirId) { + let borrower: &rustc_hir::Expr<'_>; + if let Some(addr_of_expr) = get_parent_expr_for_hir(self.cx, access_hir_id) { + if let Some(borrower_expr) = get_parent_expr(self.cx, addr_of_expr) { + borrower = borrower_expr + } else { + return; + } + } else { + return; + } + + match &borrower.kind { + hir::ExprKind::Call(func_expr, ..) => { + // We only deal with resolved paths as this is the usual case. Other expression kinds like closures + // etc. are hard to track but might be a worthy improvement in the future + if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = func_expr.kind { + self.value_mutations.push(ApplicabilityModifier::Modifier(path)); + } else { + let msg = format!( + "Unsupported borrow in Call at: {}", + SerializableSpan::from_span(self.cx, func_expr.span) + ); + self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); + } + }, + hir::ExprKind::MethodCall(..) => { + let msg = format!( + "Unsupported borrow in MethodCall at: {}", + SerializableSpan::from_span(self.cx, borrower.span) + ); + self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); + }, + _ => { + let msg = format!( + "Unexpected borrow at: {}", + SerializableSpan::from_span(self.cx, borrower.span) + ); + self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); + }, + } + } } impl<'a, 'hir> Delegate<'hir> for ValueTracker<'a, 'hir> { @@ -541,11 +584,7 @@ impl<'a, 'hir> Delegate<'hir> for ValueTracker<'a, 'hir> { fn borrow(&mut self, _place_with_id: &PlaceWithHirId<'hir>, expr_id: hir::HirId, bk: BorrowKind) { if self.is_value_expr(expr_id) { if let BorrowKind::MutBorrow = bk { - // TODO xFrednet 2021-02-17: Save the function - if let Some(hir::Node::Expr(expr)) = self.cx.tcx.hir().find(expr_id) { - let span = SerializableSpan::from_span(self.cx, expr.span); - log_to_file(&format!("- &mut {}\n", span)); - } + self.process_borrow_expr(expr_id); } } } From a39912cfbbd682270b5f3d534bc69eedfaa73a7b Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 20 Feb 2021 13:12:30 +0100 Subject: [PATCH 51/67] Metadata collection: Some refactoring for readability --- .../internal_lints/metadata_collector.rs | 104 +++++++++--------- .../track_applicability_value.rs | 4 + 2 files changed, 57 insertions(+), 51 deletions(-) diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index c8637531d9428..de94e47047b47 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -37,8 +37,8 @@ use std::path::Path; use crate::utils::internal_lints::is_lint_ref_type; use crate::utils::{ - get_enclosing_body, get_parent_expr_for_hir, last_path_segment, match_function_call, match_qpath, match_type, - path_to_local_id, paths, span_lint, walk_ptrs_ty_depth, get_parent_expr + get_enclosing_body, get_parent_expr, get_parent_expr_for_hir, last_path_segment, match_function_call, match_qpath, + match_type, path_to_local_id, paths, span_lint, walk_ptrs_ty_depth, }; /// This is the output file of the lint collector. @@ -202,39 +202,19 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector { /// ``` fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if_chain! { + // item validation if let ItemKind::Static(ref ty, Mutability::Not, body_id) = item.kind; if is_lint_ref_type(cx, ty); let expr = &cx.tcx.hir().body(body_id).value; if let ExprKind::AddrOf(_, _, ref inner_exp) = expr.kind; if let ExprKind::Struct(_, _, _) = inner_exp.kind; + // 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) = get_lint_group_or_lint(cx, &lint_name, item); + if let Some(docs) = extract_attr_docs_or_lint(cx, item); then { - let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase(); - if BLACK_LISTED_LINTS.contains(&lint_name.as_str()) { - return; - } - - let group: String; - let result = cx.lint_store.check_lint_name(lint_name.as_str(), Some(sym::clippy)); - if let CheckLintNameResult::Tool(Ok(lint_lst)) = result { - if let Some(group_some) = get_lint_group(cx, lint_lst[0]) { - group = group_some; - } else { - lint_collection_error_item(cx, item, "Unable to determine lint group"); - return; - } - } else { - lint_collection_error_item(cx, item, "Unable to find lint in lint_store"); - return; - } - - let docs: String; - if let Some(docs_some) = extract_attr_docs(item) { - docs = docs_some; - } else { - lint_collection_error_item(cx, item, "could not collect the lint documentation"); - return; - }; - self.lints.push(LintMetadata::new( lint_name, SerializableSpan::from_item(cx, item), @@ -301,7 +281,7 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector { let span = SerializableSpan::from_span(cx, local.span); let local_str = crate::utils::snippet(cx, local.span, "_"); log_to_file(&format!("{} -- {}\n", local_str, span)); - + let value_hir_id = local.pat.hir_id; let mut tracker = ValueTracker::new(cx, value_hir_id); if let Some(init_expr) = local.init { @@ -342,10 +322,21 @@ fn get_local_type<'a>(cx: &'a LateContext<'_>, local: &'a hir::Local<'_>) -> Opt None } +// ================================================================== +// 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 { + extract_attr_docs(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 /// @@ -367,6 +358,19 @@ fn extract_attr_docs(item: &Item<'_>) -> Option { }) } +fn get_lint_group_or_lint(cx: &LateContext<'_>, lint_name: &str, item: &'tcx Item<'_>) -> Option { + let result = cx.lint_store.check_lint_name(lint_name, Some(sym::clippy)); + if let CheckLintNameResult::Tool(Ok(lint_lst)) = result { + get_lint_group(cx, lint_lst[0]).or_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 { for (group_name, lints, _) in &cx.lint_store.get_lint_groups() { if lints.iter().any(|x| *x == lint_id) { @@ -526,19 +530,8 @@ impl<'a, 'hir> ValueTracker<'a, 'hir> { } } - fn process_borrow_expr(&mut self, access_hir_id: hir::HirId) { - let borrower: &rustc_hir::Expr<'_>; - if let Some(addr_of_expr) = get_parent_expr_for_hir(self.cx, access_hir_id) { - if let Some(borrower_expr) = get_parent_expr(self.cx, addr_of_expr) { - borrower = borrower_expr - } else { - return; - } - } else { - return; - } - - match &borrower.kind { + fn process_borrow_expr(&mut self, borrower_expr: &'hir rustc_hir::Expr<'hir>) { + match &borrower_expr.kind { hir::ExprKind::Call(func_expr, ..) => { // We only deal with resolved paths as this is the usual case. Other expression kinds like closures // etc. are hard to track but might be a worthy improvement in the future @@ -555,36 +548,45 @@ impl<'a, 'hir> ValueTracker<'a, 'hir> { hir::ExprKind::MethodCall(..) => { let msg = format!( "Unsupported borrow in MethodCall at: {}", - SerializableSpan::from_span(self.cx, borrower.span) + SerializableSpan::from_span(self.cx, borrower_expr.span) ); self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); }, _ => { let msg = format!( "Unexpected borrow at: {}", - SerializableSpan::from_span(self.cx, borrower.span) + SerializableSpan::from_span(self.cx, borrower_expr.span) ); self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); }, } } + + fn process_consume_expr(&mut self, consume_expr: &'hir rustc_hir::Expr<'hir>) { + // We are only interested in lint emissions. Other types like assignments might be + // interesting for further use or improvement but are to complex for now. + if let hir::ExprKind::Call(func_expr, ..) = &consume_expr.kind {} + } } impl<'a, 'hir> Delegate<'hir> for ValueTracker<'a, 'hir> { fn consume(&mut self, _place_with_id: &PlaceWithHirId<'hir>, expr_id: hir::HirId, _: ConsumeMode) { if self.is_value_expr(expr_id) { // TODO xFrednet 2021-02-17: Check if lint emission and extract lint ID - if let Some(hir::Node::Expr(expr)) = self.cx.tcx.hir().find(expr_id) { - let span = SerializableSpan::from_span(self.cx, expr.span); - log_to_file(&format!("- consume {}\n", span)); + if let Some(expr) = get_parent_expr_for_hir(self.cx, expr_id) { + log_to_file(&format!("- consume {:?}\n", expr)); } } } fn borrow(&mut self, _place_with_id: &PlaceWithHirId<'hir>, expr_id: hir::HirId, bk: BorrowKind) { - if self.is_value_expr(expr_id) { - if let BorrowKind::MutBorrow = bk { - self.process_borrow_expr(expr_id); + if_chain! { + if self.is_value_expr(expr_id); + if let BorrowKind::MutBorrow = bk; + if let Some(addr_of_expr) = get_parent_expr_for_hir(self.cx, expr_id); + if let Some(borrower_expr) = get_parent_expr(self.cx, addr_of_expr); + then { + self.process_borrow_expr(borrower_expr); } } } diff --git a/tests/ui-internal/metadata-collector/track_applicability_value.rs b/tests/ui-internal/metadata-collector/track_applicability_value.rs index 75fae623e6109..b0f59e5cf8a76 100644 --- a/tests/ui-internal/metadata-collector/track_applicability_value.rs +++ b/tests/ui-internal/metadata-collector/track_applicability_value.rs @@ -23,6 +23,8 @@ fn modifier_fn(applicability: &mut Applicability) { } } +fn consumer_fn(_applicability: Applicability) {} + struct Muh; impl Muh { @@ -43,4 +45,6 @@ fn main() { }; modifier_fn(&mut applicability); + + consumer_fn(applicability); } From ee8a99a1140880e10677fa0b03167cd364fece3f Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 20 Feb 2021 13:50:09 +0100 Subject: [PATCH 52/67] Metadata collection: Collecting Applicability assign values --- clippy_lints/Cargo.toml | 2 +- .../internal_lints/metadata_collector.rs | 361 ++++-------------- clippy_utils/Cargo.toml | 1 + clippy_utils/src/lib.rs | 8 +- 4 files changed, 73 insertions(+), 299 deletions(-) diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index d1d56fc84f948..42218c2c00ac5 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -33,7 +33,7 @@ url = { version = "2.1.0", features = ["serde"] } deny-warnings = [] # build clippy with internal lints enabled, off by default internal-lints = ["clippy_utils/internal-lints"] -metadata-collector-lint = ["serde_json"] +metadata-collector-lint = ["serde_json", "clippy_utils/metadata-collector-lint"] [package.metadata.rust-analyzer] # This crate uses #[feature(rustc_private)] diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index de94e47047b47..a16f7c2ea5e12 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -21,15 +21,11 @@ // - TODO xFrednet 2021-02-13: Collect depreciations and maybe renames use if_chain::if_chain; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::FxHashMap; use rustc_hir::{self as hir, ExprKind, Item, ItemKind, Mutability}; use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId}; -use rustc_middle::ty::{BorrowKind, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Loc, Span, Symbol}; -use rustc_trait_selection::infer::TyCtxtInferExt; -use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceWithHirId}; -use rustc_typeck::hir_ty_to_ty; use serde::Serialize; use std::fs::{self, OpenOptions}; use std::io::prelude::*; @@ -37,8 +33,7 @@ use std::path::Path; use crate::utils::internal_lints::is_lint_ref_type; use crate::utils::{ - get_enclosing_body, get_parent_expr, get_parent_expr_for_hir, last_path_segment, match_function_call, match_qpath, - match_type, path_to_local_id, paths, span_lint, walk_ptrs_ty_depth, + last_path_segment, match_function_call, match_type, paths, span_lint, walk_ptrs_ty_depth, match_path, }; /// This is the output file of the lint collector. @@ -50,11 +45,11 @@ const BLACK_LISTED_LINTS: [&str; 2] = ["lint_author", "deep_code_inspection"]; // handling #[rustfmt::skip] const LINT_EMISSION_FUNCTIONS: [&[&str]; 5] = [ - &["clippy_lints", "utils", "diagnostics", "span_lint"], - &["clippy_lints", "utils", "diagnostics", "span_lint_and_help"], - &["clippy_lints", "utils", "diagnostics", "span_lint_and_note"], - &["clippy_lints", "utils", "diagnostics", "span_lint_hir"], - &["clippy_lints", "utils", "diagnostics", "span_lint_and_sugg"], + &["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"], ]; declare_clippy_lint! { @@ -175,10 +170,10 @@ struct ApplicabilityInfo { /// [rustfix#141](https://github.com/rust-lang/rustfix/issues/141) as such suggestions can /// currently not be applied automatically. has_multi_suggestion: bool, - /// These are all the available applicability values for the lint suggestions - applicabilities: FxHashSet, + applicability: Option, } +#[allow(dead_code)] fn log_to_file(msg: &str) { let mut file = OpenOptions::new() .write(true) @@ -190,7 +185,7 @@ fn log_to_file(msg: &str) { write!(file, "{}", msg).unwrap(); } -impl<'tcx> LateLintPass<'tcx> for MetadataCollector { +impl<'hir> LateLintPass<'hir> for MetadataCollector { /// Collecting lint declarations like: /// ```rust, ignore /// declare_clippy_lint! { @@ -200,7 +195,7 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector { /// "Who am I?" /// } /// ``` - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + fn check_item(&mut self, cx: &LateContext<'hir>, item: &'hir Item<'_>) { if_chain! { // item validation if let ItemKind::Static(ref ty, Mutability::Not, body_id) = item.kind; @@ -239,87 +234,16 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector { /// Applicability::MachineApplicable, // <-- Extracts this constant value /// ); /// ``` - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + fn check_expr(&mut self, cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) { if let Some(args) = match_simple_lint_emission(cx, expr) { - if let Some((lint_name, mut applicability)) = extract_emission_info(cx, args) { + if let Some((lint_name, applicability)) = extract_emission_info(cx, args) { let app_info = self.applicability_into.entry(lint_name).or_default(); - applicability.drain(..).for_each(|x| { - app_info.applicabilities.insert(x); - }); + app_info.applicability = applicability; } else { lint_collection_error_span(cx, expr.span, "I found this but I can't get the lint or applicability"); } } } - - /// Tracking and hopefully collecting dynamic applicability values - /// - /// Example: - /// ```rust, ignore - /// // vvv Applicability value to track - /// let mut applicability = Applicability::MachineApplicable; - /// // vvv Value Mutation - /// let suggestion = snippet_with_applicability(cx, expr.span, "_", &mut applicability); - /// // vvv Emission to link the value to the lint - /// span_lint_and_sugg( - /// cx, - /// SOME_LINT, - /// expr.span, - /// "This can be improved", - /// "try", - /// suggestion, - /// applicability, - /// ); - /// ``` - fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx hir::Local<'tcx>) { - if_chain! { - if let Some(local_ty) = get_local_type(cx, local); - if match_type(cx, local_ty, &paths::APPLICABILITY); - if let Some(body) = get_enclosing_body(cx, local.hir_id); - then { - // TODO xFrednet: 2021-02-19: Remove debug code - let span = SerializableSpan::from_span(cx, local.span); - let local_str = crate::utils::snippet(cx, local.span, "_"); - log_to_file(&format!("{} -- {}\n", local_str, span)); - - let value_hir_id = local.pat.hir_id; - let mut tracker = ValueTracker::new(cx, value_hir_id); - if let Some(init_expr) = local.init { - tracker.process_assign_expr(init_expr) - } - - // TODO xFrednet 2021-02-18: Support nested bodies - // Note: The `ExprUseVisitor` only searches though one body, this means that values - // references in nested bodies like closures are not found by this simple visitor. - cx.tcx.infer_ctxt().enter(|infcx| { - let body_owner_id = cx.tcx.hir().body_owner_def_id(body.id()); - ExprUseVisitor::new( - &mut tracker, - &infcx, - body_owner_id, - cx.param_env, - cx.typeck_results() - ) - .consume_body(body); - }); - - log_to_file(&format!("{:?}\n", tracker.value_mutations)); - } - } - } -} - -fn get_local_type<'a>(cx: &'a LateContext<'_>, local: &'a hir::Local<'_>) -> Option> { - // TODO xFrednet 2021-02-14: support nested applicability (only in tuples) - if let Some(tc) = cx.maybe_typeck_results() { - if let Some(ty) = local.ty { - return Some(hir_ty_to_ty(cx.tcx, ty)); - } else if let Some(init) = local.init { - return Some(tc.expr_ty(init)); - } - } - - None } // ================================================================== @@ -358,7 +282,7 @@ fn extract_attr_docs(item: &Item<'_>) -> Option { }) } -fn get_lint_group_or_lint(cx: &LateContext<'_>, lint_name: &str, item: &'tcx Item<'_>) -> Option { +fn get_lint_group_or_lint(cx: &LateContext<'_>, lint_name: &str, item: &'hir Item<'_>) -> Option { let result = cx.lint_store.check_lint_name(lint_name, Some(sym::clippy)); if let CheckLintNameResult::Tool(Ok(lint_lst)) = result { get_lint_group(cx, lint_lst[0]).or_else(|| { @@ -405,17 +329,19 @@ fn lint_collection_error_span(cx: &LateContext<'_>, span: Span, message: &str) { // ================================================================== // Applicability // ================================================================== -fn match_simple_lint_emission<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, -) -> Option<&'tcx [hir::Expr<'tcx>]> { +/// 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_simple_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)) } /// This returns the lint name and the possible applicability of this emission -fn extract_emission_info<'tcx>(cx: &LateContext<'tcx>, args: &[hir::Expr<'_>]) -> Option<(String, Vec)> { +fn extract_emission_info<'hir>(cx: &LateContext<'hir>, args: &[hir::Expr<'_>]) -> Option<(String, Option)> { let mut lint_name = None; let mut applicability = None; @@ -425,228 +351,69 @@ fn extract_emission_info<'tcx>(cx: &LateContext<'tcx>, args: &[hir::Expr<'_>]) - if match_type(cx, arg_ty, &paths::LINT) { // If we found the lint arg, extract the lint name if let ExprKind::Path(ref lint_path) = arg.kind { - lint_name = Some(last_path_segment(lint_path).ident.name) + lint_name = Some(last_path_segment(lint_path).ident.name); } } else if match_type(cx, arg_ty, &paths::APPLICABILITY) { - if let ExprKind::Path(ref path) = arg.kind { - applicability = Some(last_path_segment(path).ident.name) - } + applicability = resolve_applicability(cx, arg); } } lint_name.map(|lint_name| { ( sym_to_string(lint_name).to_ascii_lowercase(), - applicability.map(sym_to_string).map_or_else(Vec::new, |x| vec![x]), + applicability, ) }) } -#[allow(dead_code)] -struct ValueTracker<'a, 'hir> { - cx: &'a LateContext<'hir>, - value_hir_id: hir::HirId, - value_mutations: Vec>, -} - -impl<'a, 'hir> ValueTracker<'a, 'hir> { - fn new(cx: &'a LateContext<'hir>, value_hir_id: hir::HirId) -> Self { - Self { - cx, - value_hir_id, - value_mutations: Vec::new(), - } - } - - fn is_value_expr(&self, expr_id: hir::HirId) -> bool { - match self.cx.tcx.hir().find(expr_id) { - Some(hir::Node::Expr(expr)) => path_to_local_id(expr, self.value_hir_id), - _ => false, - } - } - - /// This function extracts possible `ApplicabilityModifier` from an assign statement like this: - /// - /// ```rust, ignore - /// // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv The expression to process - /// let value = Applicability::MachineApplicable; - /// ``` - fn process_assign_expr(&mut self, expr: &'hir hir::Expr<'hir>) { - // This is a bit more complicated. I'll therefor settle on the simple solution of - // simplifying the cases we support. - match &expr.kind { - hir::ExprKind::Call(func_expr, ..) => { - // We only deal with resolved paths as this is the usual case. Other expression kinds like closures - // etc. are hard to track but might be a worthy improvement in the future - if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = func_expr.kind { - self.value_mutations.push(ApplicabilityModifier::Producer(path)); - } else { - let msg = format!( - "Unsupported assign Call expression at: {}", - SerializableSpan::from_span(self.cx, func_expr.span) - ); - self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); - } - }, - hir::ExprKind::MethodCall(..) => { - let msg = format!( - "Unsupported assign MethodCall expression at: {}", - SerializableSpan::from_span(self.cx, expr.span) - ); - self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); - }, - // We can ignore ifs without an else block because those can't be used as an assignment - hir::ExprKind::If(_con, if_block, Some(else_block)) => { - self.process_assign_expr(if_block); - self.process_assign_expr(else_block); - }, - hir::ExprKind::Match(_expr, arms, _) => { - for arm in *arms { - self.process_assign_expr(arm.body); - } - }, - hir::ExprKind::Loop(block, ..) | hir::ExprKind::Block(block, ..) => { - if let Some(block_expr) = block.expr { - self.process_assign_expr(block_expr); +fn resolve_applicability(cx: &LateContext<'hir>, expr: &hir::Expr<'_>) -> Option { + match expr.kind { + // We can ignore ifs without an else block because those can't be used as an assignment + hir::ExprKind::If(_con, _if_block, Some(_else_block)) => { + // self.process_assign_expr(if_block); + // self.process_assign_expr(else_block); + return Some("TODO IF EXPR".to_string()); + }, + hir::ExprKind::Match(_expr, _arms, _) => { + // for arm in *arms { + // self.process_assign_expr(arm.body); + // } + return Some("TODO MATCH EXPR".to_string()); + }, + hir::ExprKind::Loop(block, ..) | hir::ExprKind::Block(block, ..) => { + if let Some(block_expr) = block.expr { + return resolve_applicability(cx, block_expr); + } + }, + ExprKind::Path(hir::QPath::Resolved(_, path)) => { + // direct applicabilities are simple: + for enum_value in &paths::APPLICABILITY_VALUES { + if match_path(path, enum_value) { + return Some(enum_value[2].to_string()); } - }, - hir::ExprKind::Path(path) => { - for enum_value in &paths::APPLICABILITY_VALUES { - if match_qpath(path, enum_value) { - self.value_mutations - .push(ApplicabilityModifier::ConstValue(enum_value[2].to_string())); + } + + // Values yay + if let hir::def::Res::Local(local_hir) = path.res { + if let Some(local) = get_parent_local(cx, local_hir) { + if let Some(local_init) = local.init { + return resolve_applicability(cx, local_init); } } - }, - // hir::ExprKind::Field(expr, ident) => not supported - // hir::ExprKind::Index(expr, expr) => not supported - _ => { - let msg = format!( - "Unexpected assign expression at: {}", - SerializableSpan::from_span(self.cx, expr.span) - ); - self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); - }, - } - } - - fn process_borrow_expr(&mut self, borrower_expr: &'hir rustc_hir::Expr<'hir>) { - match &borrower_expr.kind { - hir::ExprKind::Call(func_expr, ..) => { - // We only deal with resolved paths as this is the usual case. Other expression kinds like closures - // etc. are hard to track but might be a worthy improvement in the future - if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = func_expr.kind { - self.value_mutations.push(ApplicabilityModifier::Modifier(path)); - } else { - let msg = format!( - "Unsupported borrow in Call at: {}", - SerializableSpan::from_span(self.cx, func_expr.span) - ); - self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); - } - }, - hir::ExprKind::MethodCall(..) => { - let msg = format!( - "Unsupported borrow in MethodCall at: {}", - SerializableSpan::from_span(self.cx, borrower_expr.span) - ); - self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); - }, - _ => { - let msg = format!( - "Unexpected borrow at: {}", - SerializableSpan::from_span(self.cx, borrower_expr.span) - ); - self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); - }, - } - } - - fn process_consume_expr(&mut self, consume_expr: &'hir rustc_hir::Expr<'hir>) { - // We are only interested in lint emissions. Other types like assignments might be - // interesting for further use or improvement but are to complex for now. - if let hir::ExprKind::Call(func_expr, ..) = &consume_expr.kind {} - } -} - -impl<'a, 'hir> Delegate<'hir> for ValueTracker<'a, 'hir> { - fn consume(&mut self, _place_with_id: &PlaceWithHirId<'hir>, expr_id: hir::HirId, _: ConsumeMode) { - if self.is_value_expr(expr_id) { - // TODO xFrednet 2021-02-17: Check if lint emission and extract lint ID - if let Some(expr) = get_parent_expr_for_hir(self.cx, expr_id) { - log_to_file(&format!("- consume {:?}\n", expr)); } } + _ => {} } - fn borrow(&mut self, _place_with_id: &PlaceWithHirId<'hir>, expr_id: hir::HirId, bk: BorrowKind) { - if_chain! { - if self.is_value_expr(expr_id); - if let BorrowKind::MutBorrow = bk; - if let Some(addr_of_expr) = get_parent_expr_for_hir(self.cx, expr_id); - if let Some(borrower_expr) = get_parent_expr(self.cx, addr_of_expr); - then { - self.process_borrow_expr(borrower_expr); - } - } - } - fn mutate(&mut self, _assignee_place: &PlaceWithHirId<'hir>, expr_id: hir::HirId) { - if_chain! { - if self.is_value_expr(expr_id); - if let Some(expr) = get_parent_expr_for_hir(self.cx, expr_id); - if let hir::ExprKind::Assign(_value_expr, assign_expr, ..) = expr.kind; - then { - self.process_assign_expr(assign_expr); - } - } - } + Some("TODO".to_string()) } -/// The life of a value in Rust is a true adventure. These are the corner stones of such a -/// fairy tale. Let me introduce you to the possible stepping stones a value might have in -/// in our crazy word: -#[derive(Debug)] -#[allow(dead_code)] -enum ApplicabilityModifier<'hir> { - Unknown(String), - /// A simple constant value. - /// - /// This is the actual character of a value. It's baseline. This only defines where the value - /// started. As in real life it can still change and fully decide who it wants to be. - ConstValue(String), - /// A producer is a function that returns an applicability value. - /// - /// This is the heritage of this value. This value comes from a long family tree and is not - /// just a black piece of paper. The evaluation of this stepping stone needs additional - /// context. We therefore only add a reference. This reference will later be used to ask - /// the librarian about the possible initial character that this value might have. - Producer(&'hir hir::Path<'hir>), - /// A modifier that takes the given applicability and might modify it - /// - /// What would an RPG be without it's NPCs. The special thing about modifiers is that they can - /// be actively interested in the story of the value and might make decisions based on the - /// character of this hero. This means that a modifier doesn't just force its way into the life - /// of our hero but it actually asks him how he's been. The possible modification is a result - /// of the situation. - /// - /// Take this part of our heroes life very seriously! - Modifier(&'hir hir::Path<'hir>), - /// The actual emission of a lint - /// - /// All good things must come to an end. Even the life of your awesome applicability hero. He - /// was the bravest soul that has ever wondered this earth. Songs will be written about his - /// heroic deeds. Castles will be named after him and the world how we know it will never be - /// the same! - /// - /// Is this a happy ending? Did he archive what he wanted in his life? Yes, YES, he has lived a - /// life and he will continue to live in all the lint suggestions that can be applied or just - /// displayed by Clippy. He might be no more, but his legacy will serve generations to come. - LintEmit(LintEmission), -} +fn get_parent_local(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> { + let map = cx.tcx.hir(); + if let Some(hir::Node::Local(local)) = map.find(map.get_parent_node(hir_id)) { + return Some(local); + } -#[derive(Debug)] -struct LintEmission { - lint: String, - is_multi_line_sugg: bool, + None } diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index d04c5f889dda0..6e158c8ce7239 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -15,6 +15,7 @@ rustc-semver="1.1.0" [features] internal-lints = [] +metadata-collector-lint = [] [package.metadata.rust-analyzer] # This crate uses #[feature(rustc_private)] diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 94eade0c93243..d4bc42657f48e 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -821,7 +821,13 @@ pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option> { /// 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>> { - match get_parent_node(cx.tcx, e.hir_id) { + 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, } From 5830fa7c6064480863d1f90513c0c9b05630a9d3 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 27 Feb 2021 18:39:02 +0100 Subject: [PATCH 53/67] Metadata Collection: Collecting direct emission applicabilities (324/455) --- .../internal_lints/metadata_collector.rs | 126 +++++++++++++----- 1 file changed, 93 insertions(+), 33 deletions(-) diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index a16f7c2ea5e12..964ddb36e26e0 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -14,16 +14,19 @@ //! - [ ] TODO The Applicability for each lint for [#4310](https://github.com/rust-lang/rust-clippy/issues/4310) // # Applicability -// - TODO xFrednet 2021-01-17: Find all methods that take and modify applicability or predefine -// them? // - TODO xFrednet 2021-01-17: Find lint emit and collect applicability +// - TODO xFrednet 2021-02-27: Link applicability from function parameters +// - (Examples: suspicious_operation_groupings:267, needless_bool.rs:311) +// - TODO xFrednet 2021-02-27: Tuple if let thingy +// - (Examples: unused_unit.rs:140, misc.rs:694) // # NITs // - TODO xFrednet 2021-02-13: Collect depreciations and maybe renames use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::{self as hir, ExprKind, Item, ItemKind, Mutability}; +use rustc_hir::{self as hir, intravisit, ExprKind, Item, ItemKind, Mutability}; 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::Serialize; @@ -33,13 +36,15 @@ use std::path::Path; use crate::utils::internal_lints::is_lint_ref_type; use crate::utils::{ - last_path_segment, match_function_call, match_type, paths, span_lint, walk_ptrs_ty_depth, match_path, + last_path_segment, match_function_call, match_path, match_type, paths, span_lint, walk_ptrs_ty_depth, }; /// This is the output file of the lint collector. const OUTPUT_FILE: &str = "metadata_collection.json"; /// These lints are excluded from the export. const BLACK_LISTED_LINTS: [&str; 2] = ["lint_author", "deep_code_inspection"]; +/// These groups will be ignored by the lint group matcher +const BLACK_LISTED_LINT_GROUP: [&str; 1] = ["clippy::all"]; // TODO xFrednet 2021-02-15: `span_lint_and_then` & `span_lint_hir_and_then` requires special // handling @@ -52,6 +57,9 @@ const LINT_EMISSION_FUNCTIONS: [&[&str]; 5] = [ &["clippy_utils", "diagnostics", "span_lint_and_sugg"], ]; +/// The index of the applicability name of `paths::APPLICABILITY_VALUES` +const APPLICABILITY_NAME_INDEX: usize = 2; + declare_clippy_lint! { /// **What it does:** Collects metadata about clippy lints for the website. /// @@ -297,6 +305,10 @@ fn get_lint_group_or_lint(cx: &LateContext<'_>, lint_name: &str, item: &'hir Ite fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option { for (group_name, lints, _) in &cx.lint_store.get_lint_groups() { + if BLACK_LISTED_LINT_GROUP.contains(group_name) { + continue; + } + if lints.iter().any(|x| *x == lint_id) { return Some((*group_name).to_string()); } @@ -341,7 +353,10 @@ fn match_simple_lint_emission<'hir>( } /// This returns the lint name and the possible applicability of this emission -fn extract_emission_info<'hir>(cx: &LateContext<'hir>, args: &[hir::Expr<'_>]) -> Option<(String, Option)> { +fn extract_emission_info<'hir>( + cx: &LateContext<'hir>, + args: &'hir [hir::Expr<'hir>], +) -> Option<(String, Option)> { let mut lint_name = None; let mut applicability = None; @@ -358,41 +373,38 @@ fn extract_emission_info<'hir>(cx: &LateContext<'hir>, args: &[hir::Expr<'_>]) - } } - lint_name.map(|lint_name| { - ( - sym_to_string(lint_name).to_ascii_lowercase(), - applicability, - ) - }) + lint_name.map(|lint_name| (sym_to_string(lint_name).to_ascii_lowercase(), applicability)) } -fn resolve_applicability(cx: &LateContext<'hir>, expr: &hir::Expr<'_>) -> Option { +/// This function tries to resolve the linked applicability to the given expression. +fn resolve_applicability(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option { match expr.kind { // We can ignore ifs without an else block because those can't be used as an assignment - hir::ExprKind::If(_con, _if_block, Some(_else_block)) => { - // self.process_assign_expr(if_block); - // self.process_assign_expr(else_block); - return Some("TODO IF EXPR".to_string()); + hir::ExprKind::If(_con, if_block, Some(else_block)) => { + let mut visitor = ApplicabilityVisitor::new(cx); + intravisit::walk_expr(&mut visitor, if_block); + intravisit::walk_expr(&mut visitor, else_block); + visitor.complete() }, - hir::ExprKind::Match(_expr, _arms, _) => { - // for arm in *arms { - // self.process_assign_expr(arm.body); - // } - return Some("TODO MATCH EXPR".to_string()); + hir::ExprKind::Match(_expr, arms, _) => { + let mut visitor = ApplicabilityVisitor::new(cx); + arms.iter() + .for_each(|arm| intravisit::walk_expr(&mut visitor, arm.body)); + visitor.complete() }, hir::ExprKind::Loop(block, ..) | hir::ExprKind::Block(block, ..) => { - if let Some(block_expr) = block.expr { - return resolve_applicability(cx, block_expr); - } + let mut visitor = ApplicabilityVisitor::new(cx); + intravisit::walk_block(&mut visitor, block); + visitor.complete() }, ExprKind::Path(hir::QPath::Resolved(_, path)) => { // direct applicabilities are simple: for enum_value in &paths::APPLICABILITY_VALUES { if match_path(path, enum_value) { - return Some(enum_value[2].to_string()); + return Some(enum_value[APPLICABILITY_NAME_INDEX].to_string()); } } - + // Values yay if let hir::def::Res::Local(local_hir) = path.res { if let Some(local) = get_parent_local(cx, local_hir) { @@ -401,19 +413,67 @@ fn resolve_applicability(cx: &LateContext<'hir>, expr: &hir::Expr<'_>) -> Option } } } + + // This is true for paths that are linked to function parameters. They might be a bit more work so + // not today :) + None + }, + _ => None, + } +} + +fn get_parent_local(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(cx, pattern.hir_id), + _ => None, + } +} + +/// This visitor finds the highest applicability value in the visited expressions +struct ApplicabilityVisitor<'a, 'hir> { + cx: &'a LateContext<'hir>, + /// This is the index of hightest `Applicability` for + /// `clippy_utils::paths::APPLICABILITY_VALUES` + applicability_index: Option, +} + +impl<'a, 'hir> ApplicabilityVisitor<'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 = self + .applicability_index + .map_or(new_index, |old_index| old_index.min(new_index)) + .into(); + } - Some("TODO".to_string()) + fn complete(self) -> Option { + self.applicability_index + .map(|index| paths::APPLICABILITY_VALUES[index][APPLICABILITY_NAME_INDEX].to_string()) + } } -fn get_parent_local(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> { - let map = cx.tcx.hir(); - if let Some(hir::Node::Local(local)) = map.find(map.get_parent_node(hir_id)) { - return Some(local); +impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityVisitor<'a, 'hir> { + type Map = Map<'hir>; + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::All(self.cx.tcx.hir()) } - None + fn visit_path(&mut self, path: &hir::Path<'_>, _id: hir::HirId) { + for (index, enum_value) in paths::APPLICABILITY_VALUES.iter().enumerate() { + if match_path(path, enum_value) { + self.add_new_index(index); + break; + } + } + } } From 6658db1044bbd19e70bfbf1b74b39bd16d3c4a2c Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sun, 28 Feb 2021 16:11:45 +0100 Subject: [PATCH 54/67] Metadata collection: processing emission closures (417/455) --- .../src/slow_vector_initialization.rs | 20 +- .../internal_lints/metadata_collector.rs | 197 ++++++++++++++++-- clippy_utils/src/paths.rs | 2 + 3 files changed, 190 insertions(+), 29 deletions(-) diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index 191781be000cf..5a51e1141fba2 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -157,26 +157,16 @@ impl SlowVectorInit { vec_alloc: &VecAllocation<'_>, ) { match initialization { - InitializationType::Extend(e) | InitializationType::Resize(e) => Self::emit_lint( - cx, - e, - vec_alloc, - "slow zero-filling initialization", - SLOW_VECTOR_INITIALIZATION, - ), + InitializationType::Extend(e) | InitializationType::Resize(e) => { + Self::emit_lint(cx, e, vec_alloc, "slow zero-filling initialization") + }, }; } - fn emit_lint<'tcx>( - cx: &LateContext<'tcx>, - slow_fill: &Expr<'_>, - vec_alloc: &VecAllocation<'_>, - msg: &str, - lint: &'static Lint, - ) { + fn emit_lint<'tcx>(cx: &LateContext<'tcx>, slow_fill: &Expr<'_>, vec_alloc: &VecAllocation<'_>, msg: &str) { let len_expr = Sugg::hir(cx, vec_alloc.len_expr, "len"); - span_lint_and_then(cx, lint, slow_fill.span, msg, |diag| { + span_lint_and_then(cx, SLOW_VECTOR_INITIALIZATION, slow_fill.span, msg, |diag| { diag.span_suggestion( vec_alloc.allocation_expr.span, "consider replace allocation with", diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 964ddb36e26e0..20bd2a669aa4e 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -15,16 +15,25 @@ // # Applicability // - TODO xFrednet 2021-01-17: Find lint emit and collect applicability -// - TODO xFrednet 2021-02-27: Link applicability from function parameters -// - (Examples: suspicious_operation_groupings:267, needless_bool.rs:311) -// - TODO xFrednet 2021-02-27: Tuple if let thingy -// - (Examples: unused_unit.rs:140, misc.rs:694) +// - TODO xFrednet 2021-02-28: 1x reference to closure +// - See clippy_lints/src/needless_pass_by_value.rs@NeedlessPassByValue::check_fn +// - TODO xFrednet 2021-02-28: 4x weird emission forwarding +// - See clippy_lints/src/enum_variants.rs@EnumVariantNames::check_name +// - TODO xFrednet 2021-02-28: 6x emission forwarding with local that is initializes from +// function. +// - See clippy_lints/src/methods/mod.rs@lint_binary_expr_with_method_call +// - TODO xFrednet 2021-02-28: 2x lint from local from function call +// - See clippy_lints/src/misc.rs@check_binary +// - TODO xFrednet 2021-02-28: 2x lint from local from method call +// - See clippy_lints/src/non_copy_const.rs@lint +// - TODO xFrednet 2021-02-28: 20x lint from local +// - See clippy_lints/src/map_unit_fn.rs@lint_map_unit_fn // # NITs // - TODO xFrednet 2021-02-13: Collect depreciations and maybe renames use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::{self as hir, intravisit, ExprKind, Item, ItemKind, Mutability}; +use rustc_hir::{self as hir, intravisit, 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}; @@ -44,18 +53,36 @@ const OUTPUT_FILE: &str = "metadata_collection.json"; /// These lints are excluded from the export. const BLACK_LISTED_LINTS: [&str; 2] = ["lint_author", "deep_code_inspection"]; /// These groups will be ignored by the lint group matcher -const BLACK_LISTED_LINT_GROUP: [&str; 1] = ["clippy::all"]; +const BLACK_LISTED_LINT_GROUP: [&str; 1] = ["clippy::all", "clippy::internal"]; // TODO xFrednet 2021-02-15: `span_lint_and_then` & `span_lint_hir_and_then` requires special // handling -#[rustfmt::skip] -const LINT_EMISSION_FUNCTIONS: [&[&str]; 5] = [ +const SIMPLE_LINT_EMISSION_FUNCTIONS: [&[&str]; 5] = [ &["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"], ]; +const COMPLEX_LINT_EMISSION_FUNCTIONS: [&[&str]; 2] = [ + &["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", "mutispan_sugg"], + &["clippy_utils", "diagnostics", "multispan_sugg_with_applicability"], +]; /// The index of the applicability name of `paths::APPLICABILITY_VALUES` const APPLICABILITY_NAME_INDEX: usize = 2; @@ -177,7 +204,7 @@ 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. - has_multi_suggestion: bool, + is_multi_suggestion: bool, applicability: Option, } @@ -250,6 +277,14 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { } else { lint_collection_error_span(cx, expr.span, "I found this but I can't get the lint or applicability"); } + } else if let Some(args) = match_complex_lint_emission(cx, expr) { + if let Some((lint_name, applicability, is_multi_span)) = extract_complex_emission_info(cx, args) { + let app_info = self.applicability_into.entry(lint_name).or_default(); + app_info.applicability = applicability; + app_info.is_multi_suggestion = is_multi_span; + } else { + lint_collection_error_span(cx, expr.span, "Look, here ... I have no clue what todo with it"); + } } } } @@ -347,7 +382,16 @@ fn match_simple_lint_emission<'hir>( cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>, ) -> Option<&'hir [hir::Expr<'hir>]> { - LINT_EMISSION_FUNCTIONS + SIMPLE_LINT_EMISSION_FUNCTIONS + .iter() + .find_map(|emission_fn| match_function_call(cx, expr, emission_fn)) +} + +fn match_complex_lint_emission<'hir>( + cx: &LateContext<'hir>, + expr: &'hir hir::Expr<'_>, +) -> Option<&'hir [hir::Expr<'hir>]> { + COMPLEX_LINT_EMISSION_FUNCTIONS .iter() .find_map(|emission_fn| match_function_call(cx, expr, emission_fn)) } @@ -376,28 +420,60 @@ fn extract_emission_info<'hir>( lint_name.map(|lint_name| (sym_to_string(lint_name).to_ascii_lowercase(), applicability)) } +fn extract_complex_emission_info<'hir>( + cx: &LateContext<'hir>, + args: &'hir [hir::Expr<'hir>], +) -> Option<(String, Option, bool)> { + let mut lint_name = None; + let mut applicability = None; + let mut multi_span = 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 + if let ExprKind::Path(ref lint_path) = arg.kind { + lint_name = Some(last_path_segment(lint_path).ident.name); + } + } else if arg_ty.is_closure() { + if let ExprKind::Closure(_, _, body_id, _, _) = arg.kind { + let mut visitor = EmissionClosureVisitor::new(cx); + intravisit::walk_body(&mut visitor, cx.tcx.hir().body(body_id)); + multi_span = visitor.found_multi_span(); + applicability = visitor.complete(); + } else { + // TODO xfrednet 2021-02-28: linked closures, see: needless_pass_by_value.rs:292 + return None; + } + } + } + + lint_name.map(|lint_name| (sym_to_string(lint_name).to_ascii_lowercase(), applicability, multi_span)) +} + /// This function tries to resolve the linked applicability to the given expression. fn resolve_applicability(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option { match expr.kind { // We can ignore ifs without an else block because those can't be used as an assignment - hir::ExprKind::If(_con, if_block, Some(else_block)) => { + ExprKind::If(_con, if_block, Some(else_block)) => { let mut visitor = ApplicabilityVisitor::new(cx); intravisit::walk_expr(&mut visitor, if_block); intravisit::walk_expr(&mut visitor, else_block); visitor.complete() }, - hir::ExprKind::Match(_expr, arms, _) => { + ExprKind::Match(_expr, arms, _) => { let mut visitor = ApplicabilityVisitor::new(cx); arms.iter() .for_each(|arm| intravisit::walk_expr(&mut visitor, arm.body)); visitor.complete() }, - hir::ExprKind::Loop(block, ..) | hir::ExprKind::Block(block, ..) => { + ExprKind::Loop(block, ..) | ExprKind::Block(block, ..) => { let mut visitor = ApplicabilityVisitor::new(cx); intravisit::walk_block(&mut visitor, block); visitor.complete() }, - ExprKind::Path(hir::QPath::Resolved(_, path)) => { + ExprKind::Path(QPath::Resolved(_, path)) => { // direct applicabilities are simple: for enum_value in &paths::APPLICABILITY_VALUES { if match_path(path, enum_value) { @@ -477,3 +553,96 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityVisitor<'a, 'hir> { } } } + +/// This visitor finds the highest applicability value in the visited expressions +struct EmissionClosureVisitor<'a, 'hir> { + cx: &'a LateContext<'hir>, + /// This is the index of hightest `Applicability` for + /// `clippy_utils::paths::APPLICABILITY_VALUES` + applicability_index: Option, + suggestion_count: usize, +} + +impl<'a, 'hir> EmissionClosureVisitor<'a, 'hir> { + fn new(cx: &'a LateContext<'hir>) -> Self { + Self { + cx, + applicability_index: None, + suggestion_count: 0, + } + } + + fn add_new_index(&mut self, new_index: usize) { + self.applicability_index = self + .applicability_index + .map_or(new_index, |old_index| old_index.min(new_index)) + .into(); + } + + fn found_multi_span(&self) -> bool { + self.suggestion_count > 1 + } + + fn complete(self) -> Option { + self.applicability_index + .map(|index| paths::APPLICABILITY_VALUES[index][APPLICABILITY_NAME_INDEX].to_string()) + } +} + +impl<'a, 'hir> intravisit::Visitor<'hir> for EmissionClosureVisitor<'a, 'hir> { + type Map = Map<'hir>; + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::All(self.cx.tcx.hir()) + } + + fn visit_path(&mut self, path: &hir::Path<'_>, _id: hir::HirId) { + for (index, enum_value) in paths::APPLICABILITY_VALUES.iter().enumerate() { + if match_path(path, enum_value) { + self.add_new_index(index); + break; + } + } + } + + fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) { + 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.suggestion_count += 2; + } + }, + 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(); + let found_suggestion = + SUGGESTION_DIAGNOSTIC_BUILDER_METHODS + .iter() + .find_map(|(method_name, is_multi_part)| { + if *method_name == called_method { + Some(*is_multi_part) + } else { + None + } + }); + if let Some(multi_part) = found_suggestion { + if multi_part { + // two is enough to have it marked as a multipart suggestion + self.suggestion_count += 2; + } else { + self.suggestion_count += 1; + } + } + } + }, + _ => {}, + } + + intravisit::walk_expr(self, expr); + } +} diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 46f58b788e6eb..a6292b87768ab 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -14,6 +14,8 @@ pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [ ["rustc_lint_defs", "Applicability", "HasPlaceholders"], ["rustc_lint_defs", "Applicability", "Unspecified"], ]; +#[cfg(feature = "metadata-collector-lint")] +pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"]; pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"]; pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"]; pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"]; From 2ce5e368d84887a5a2cee669b467160d4ad7b933 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Mon, 1 Mar 2021 12:25:14 +0100 Subject: [PATCH 55/67] Metadata collection: Refining the implementation --- .gitignore | 2 +- .../src/slow_vector_initialization.rs | 2 +- .../internal_lints/metadata_collector.rs | 280 +- metadata_collection.json | 5698 +++++++++++++++++ 4 files changed, 5809 insertions(+), 173 deletions(-) create mode 100644 metadata_collection.json diff --git a/.gitignore b/.gitignore index 523bab1882836..565327a7f839c 100644 --- a/.gitignore +++ b/.gitignore @@ -29,7 +29,7 @@ out # gh pages docs util/gh-pages/lints.json -**/metadata_collection.json +# **/metadata_collection.json # rustfmt backups *.rs.bk diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index 5a51e1141fba2..a9ae2b77119bc 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -7,7 +7,7 @@ use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_block, walk_expr, walk_stmt, NestedVisitorMap, Visitor}; use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, PatKind, QPath, Stmt, StmtKind}; -use rustc_lint::{LateContext, LateLintPass, Lint}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::sym; diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 20bd2a669aa4e..b2cc637227790 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -33,7 +33,7 @@ use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::{self as hir, intravisit, ExprKind, Item, ItemKind, Mutability, QPath}; +use rustc_hir::{self as hir, 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}; @@ -52,19 +52,18 @@ use crate::utils::{ const OUTPUT_FILE: &str = "metadata_collection.json"; /// These lints are excluded from the export. const BLACK_LISTED_LINTS: [&str; 2] = ["lint_author", "deep_code_inspection"]; -/// These groups will be ignored by the lint group matcher -const BLACK_LISTED_LINT_GROUP: [&str; 1] = ["clippy::all", "clippy::internal"]; +/// 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 +const EXCLUDED_LINT_GROUPS: [&str; 1] = ["clippy::internal"]; -// TODO xFrednet 2021-02-15: `span_lint_and_then` & `span_lint_hir_and_then` requires special -// handling -const SIMPLE_LINT_EMISSION_FUNCTIONS: [&[&str]; 5] = [ +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"], -]; -const COMPLEX_LINT_EMISSION_FUNCTIONS: [&[&str]; 2] = [ &["clippy_utils", "diagnostics", "span_lint_and_then"], &["clippy_utils", "diagnostics", "span_lint_hir_and_then"], ]; @@ -270,18 +269,11 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { /// ); /// ``` fn check_expr(&mut self, cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) { - if let Some(args) = match_simple_lint_emission(cx, expr) { - if let Some((lint_name, applicability)) = extract_emission_info(cx, args) { + if let Some(args) = match_lint_emission(cx, expr) { + if let Some((lint_name, applicability, is_multi_part)) = extract_complex_emission_info(cx, args) { let app_info = self.applicability_into.entry(lint_name).or_default(); app_info.applicability = applicability; - } else { - lint_collection_error_span(cx, expr.span, "I found this but I can't get the lint or applicability"); - } - } else if let Some(args) = match_complex_lint_emission(cx, expr) { - if let Some((lint_name, applicability, is_multi_span)) = extract_complex_emission_info(cx, args) { - let app_info = self.applicability_into.entry(lint_name).or_default(); - app_info.applicability = applicability; - app_info.is_multi_suggestion = is_multi_span; + app_info.is_multi_suggestion = is_multi_part; } else { lint_collection_error_span(cx, expr.span, "Look, here ... I have no clue what todo with it"); } @@ -292,7 +284,6 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { // ================================================================== // Lint definition extraction // ================================================================== - fn sym_to_string(sym: Symbol) -> String { sym.as_str().to_string() } @@ -328,10 +319,12 @@ fn extract_attr_docs(item: &Item<'_>) -> Option { fn get_lint_group_or_lint(cx: &LateContext<'_>, lint_name: &str, item: &'hir Item<'_>) -> Option { let result = cx.lint_store.check_lint_name(lint_name, Some(sym::clippy)); if let CheckLintNameResult::Tool(Ok(lint_lst)) = result { - get_lint_group(cx, lint_lst[0]).or_else(|| { - lint_collection_error_item(cx, item, "Unable to determine lint group"); - None - }) + get_lint_group(cx, lint_lst[0]) + .or_else(|| { + lint_collection_error_item(cx, item, "Unable to determine lint group"); + None + }) + .filter(|group| !EXCLUDED_LINT_GROUPS.contains(&group.as_str())) } else { lint_collection_error_item(cx, item, "Unable to find lint in lint_store"); None @@ -340,7 +333,7 @@ fn get_lint_group_or_lint(cx: &LateContext<'_>, lint_name: &str, item: &'hir Ite fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option { for (group_name, lints, _) in &cx.lint_store.get_lint_groups() { - if BLACK_LISTED_LINT_GROUP.contains(group_name) { + if IGNORED_LINT_GROUPS.contains(group_name) { continue; } @@ -378,55 +371,19 @@ fn lint_collection_error_span(cx: &LateContext<'_>, span: Span, message: &str) { // ================================================================== /// 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_simple_lint_emission<'hir>( - cx: &LateContext<'hir>, - expr: &'hir hir::Expr<'_>, -) -> Option<&'hir [hir::Expr<'hir>]> { - SIMPLE_LINT_EMISSION_FUNCTIONS - .iter() - .find_map(|emission_fn| match_function_call(cx, expr, emission_fn)) -} - -fn match_complex_lint_emission<'hir>( - cx: &LateContext<'hir>, - expr: &'hir hir::Expr<'_>, -) -> Option<&'hir [hir::Expr<'hir>]> { - COMPLEX_LINT_EMISSION_FUNCTIONS +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)) } -/// This returns the lint name and the possible applicability of this emission -fn extract_emission_info<'hir>( - cx: &LateContext<'hir>, - args: &'hir [hir::Expr<'hir>], -) -> Option<(String, Option)> { - let mut lint_name = None; - let mut applicability = None; - - 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 - if let ExprKind::Path(ref lint_path) = arg.kind { - lint_name = Some(last_path_segment(lint_path).ident.name); - } - } else if match_type(cx, arg_ty, &paths::APPLICABILITY) { - applicability = resolve_applicability(cx, arg); - } - } - - lint_name.map(|lint_name| (sym_to_string(lint_name).to_ascii_lowercase(), applicability)) -} - fn extract_complex_emission_info<'hir>( cx: &LateContext<'hir>, args: &'hir [hir::Expr<'hir>], ) -> Option<(String, Option, bool)> { let mut lint_name = None; let mut applicability = None; - let mut multi_span = false; + let mut multi_part = false; for arg in args { let (arg_ty, _) = walk_ptrs_ty_depth(cx.typeck_results().expr_ty(&arg)); @@ -436,87 +393,47 @@ fn extract_complex_emission_info<'hir>( if let ExprKind::Path(ref lint_path) = arg.kind { lint_name = Some(last_path_segment(lint_path).ident.name); } + } else if match_type(cx, arg_ty, &paths::APPLICABILITY) { + applicability = resolve_applicability(cx, arg); } else if arg_ty.is_closure() { - if let ExprKind::Closure(_, _, body_id, _, _) = arg.kind { - let mut visitor = EmissionClosureVisitor::new(cx); - intravisit::walk_body(&mut visitor, cx.tcx.hir().body(body_id)); - multi_span = visitor.found_multi_span(); - applicability = visitor.complete(); - } else { - // TODO xfrednet 2021-02-28: linked closures, see: needless_pass_by_value.rs:292 - return None; - } + 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)); } } - lint_name.map(|lint_name| (sym_to_string(lint_name).to_ascii_lowercase(), applicability, multi_span)) + lint_name.map(|lint_name| (sym_to_string(lint_name).to_ascii_lowercase(), applicability, multi_part)) } /// This function tries to resolve the linked applicability to the given expression. fn resolve_applicability(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option { - match expr.kind { - // We can ignore ifs without an else block because those can't be used as an assignment - ExprKind::If(_con, if_block, Some(else_block)) => { - let mut visitor = ApplicabilityVisitor::new(cx); - intravisit::walk_expr(&mut visitor, if_block); - intravisit::walk_expr(&mut visitor, else_block); - visitor.complete() - }, - ExprKind::Match(_expr, arms, _) => { - let mut visitor = ApplicabilityVisitor::new(cx); - arms.iter() - .for_each(|arm| intravisit::walk_expr(&mut visitor, arm.body)); - visitor.complete() - }, - ExprKind::Loop(block, ..) | ExprKind::Block(block, ..) => { - let mut visitor = ApplicabilityVisitor::new(cx); - intravisit::walk_block(&mut visitor, block); - visitor.complete() - }, - ExprKind::Path(QPath::Resolved(_, path)) => { - // direct applicabilities are simple: - for enum_value in &paths::APPLICABILITY_VALUES { - if match_path(path, enum_value) { - return Some(enum_value[APPLICABILITY_NAME_INDEX].to_string()); - } - } - - // Values yay - if let hir::def::Res::Local(local_hir) = path.res { - if let Some(local) = get_parent_local(cx, local_hir) { - if let Some(local_init) = local.init { - return resolve_applicability(cx, local_init); - } - } - } - - // This is true for paths that are linked to function parameters. They might be a bit more work so - // not today :) - None - }, - _ => None, - } + let mut resolver = ApplicabilityResolver::new(cx); + resolver.visit_expr(expr); + resolver.complete() } -fn get_parent_local(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(cx, pattern.hir_id), - _ => None, +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 } /// This visitor finds the highest applicability value in the visited expressions -struct ApplicabilityVisitor<'a, 'hir> { +struct ApplicabilityResolver<'a, 'hir> { cx: &'a LateContext<'hir>, - /// This is the index of hightest `Applicability` for - /// `clippy_utils::paths::APPLICABILITY_VALUES` + /// This is the index of hightest `Applicability` for `paths::APPLICABILITY_VALUES` applicability_index: Option, } -impl<'a, 'hir> ApplicabilityVisitor<'a, 'hir> { +impl<'a, 'hir> ApplicabilityResolver<'a, 'hir> { fn new(cx: &'a LateContext<'hir>) -> Self { Self { cx, @@ -537,75 +454,104 @@ impl<'a, 'hir> ApplicabilityVisitor<'a, 'hir> { } } -impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityVisitor<'a, 'hir> { +impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityResolver<'a, 'hir> { type Map = Map<'hir>; fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { intravisit::NestedVisitorMap::All(self.cx.tcx.hir()) } - fn visit_path(&mut self, path: &hir::Path<'_>, _id: hir::HirId) { + 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); - break; + 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); } + }; + + // TODO xFrednet 2021-03-01: support function arguments? + + intravisit::walk_expr(self, expr); + } +} + +/// This returns the parent local node if the expression is a reference to +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 EmissionClosureVisitor<'a, 'hir> { +struct IsMultiSpanScanner<'a, 'hir> { cx: &'a LateContext<'hir>, - /// This is the index of hightest `Applicability` for - /// `clippy_utils::paths::APPLICABILITY_VALUES` - applicability_index: Option, suggestion_count: usize, } -impl<'a, 'hir> EmissionClosureVisitor<'a, 'hir> { +impl<'a, 'hir> IsMultiSpanScanner<'a, 'hir> { fn new(cx: &'a LateContext<'hir>) -> Self { Self { cx, - applicability_index: None, suggestion_count: 0, } } - fn add_new_index(&mut self, new_index: usize) { - self.applicability_index = self - .applicability_index - .map_or(new_index, |old_index| old_index.min(new_index)) - .into(); + /// Add a new single expression suggestion to the counter + fn add_singe_span_suggestion(&mut self) { + self.suggestion_count += 1; } - fn found_multi_span(&self) -> bool { - 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; } - fn complete(self) -> Option { - self.applicability_index - .map(|index| paths::APPLICABILITY_VALUES[index][APPLICABILITY_NAME_INDEX].to_string()) + /// Checks if the suggestions include multiple spanns + fn is_multi_part(&self) -> bool { + self.suggestion_count > 1 } } -impl<'a, 'hir> intravisit::Visitor<'hir> for EmissionClosureVisitor<'a, 'hir> { +impl<'a, 'hir> intravisit::Visitor<'hir> for IsMultiSpanScanner<'a, 'hir> { type Map = Map<'hir>; fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { intravisit::NestedVisitorMap::All(self.cx.tcx.hir()) } - fn visit_path(&mut self, path: &hir::Path<'_>, _id: hir::HirId) { - for (index, enum_value) in paths::APPLICABILITY_VALUES.iter().enumerate() { - if match_path(path, enum_value) { - self.add_new_index(index); - break; - } + 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; } - } - fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) { match &expr.kind { ExprKind::Call(fn_expr, _args) => { let found_function = SUGGESTION_FUNCTIONS @@ -613,29 +559,21 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for EmissionClosureVisitor<'a, 'hir> { .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.suggestion_count += 2; + self.add_singe_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(); - let found_suggestion = - SUGGESTION_DIAGNOSTIC_BUILDER_METHODS - .iter() - .find_map(|(method_name, is_multi_part)| { - if *method_name == called_method { - Some(*is_multi_part) - } else { - None - } - }); - if let Some(multi_part) = found_suggestion { - if multi_part { - // two is enough to have it marked as a multipart suggestion - self.suggestion_count += 2; - } else { - self.suggestion_count += 1; + 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_singe_span_suggestion(); + } + break; } } } diff --git a/metadata_collection.json b/metadata_collection.json new file mode 100644 index 0000000000000..507752a782cf3 --- /dev/null +++ b/metadata_collection.json @@ -0,0 +1,5698 @@ +[ + { + "id": "approx_constant", + "id_span": { + "path": "clippy_lints/src/approx_const.rs", + "line": 34 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for floating point literals that approximate constants which are defined in\n [`std::f32::consts`](https://doc.rust-lang.org/stable/std/f32/consts/#constants)\n or\n [`std::f64::consts`](https://doc.rust-lang.org/stable/std/f64/consts/#constants),\n respectively, suggesting to use the predefined constant.\n\n **Why is this bad?** Usually, the definition in the standard library is more\n precise than what people come up with. If you find that your definition is\n actually more precise, please [file a Rust\n issue](https://github.com/rust-lang/rust/issues).\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = 3.14;\n let y = 1_f64 / x;\n ```\n Use predefined constants instead:\n ```rust\n let x = std::f32::consts::PI;\n let y = std::f64::consts::FRAC_1_PI;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "integer_arithmetic", + "id_span": { + "path": "clippy_lints/src/arithmetic.rs", + "line": 28 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for integer arithmetic operations which could overflow or panic.\n Specifically, checks for any operators (`+`, `-`, `*`, `<<`, etc) which are capable\n of overflowing according to the [Rust\n Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow),\n or which can panic (`/`, `%`). No bounds analysis or sophisticated reasoning is\n attempted.\n\n **Why is this bad?** Integer overflow will trigger a panic in debug builds or will wrap in\n release mode. Division by zero will cause a panic in either mode. In some applications one\n wants explicitly checked, wrapping or saturating arithmetic.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let a = 0;\n a + 1;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "float_arithmetic", + "id_span": { + "path": "clippy_lints/src/arithmetic.rs", + "line": 46 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for float arithmetic.\n **Why is this bad?** For some embedded systems or kernel development, it\n can be useful to rule out floating-point numbers.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let a = 0.0;\n a + 1.0;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "as_conversions", + "id_span": { + "path": "clippy_lints/src/as_conversions.rs", + "line": 42 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for usage of `as` conversions.\n Note that this lint is specialized in linting *every single* use of `as`\n regardless of whether good alternatives exist or not.\n If you want more precise lints for `as`, please consider using these separate lints:\n `unnecessary_cast`, `cast_lossless/possible_truncation/possible_wrap/precision_loss/sign_loss`,\n `fn_to_numeric_cast(_with_truncation)`, `char_lit_as_u8`, `ref_to_mut` and `ptr_as_ptr`.\n There is a good explanation the reason why this lint should work in this way and how it is useful\n [in this issue](https://github.com/rust-lang/rust-clippy/issues/5122).\n\n **Why is this bad?** `as` conversions will perform many kinds of\n conversions, including silently lossy conversions and dangerous coercions.\n There are cases when it makes sense to use `as`, so the lint is\n Allow by default.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n let a: u32;\n ...\n f(a as u16);\n ```\n\n Usually better represents the semantics you expect:\n ```rust,ignore\n f(a.try_into()?);\n ```\n or\n ```rust,ignore\n f(a.try_into().expect(\"Unexpected u16 overflow in f\"));\n ```\n\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "inline_asm_x86_intel_syntax", + "id_span": { + "path": "clippy_lints/src/asm_syntax.rs", + "line": 78 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for usage of Intel x86 assembly syntax.\n **Why is this bad?** The lint has been enabled to indicate a preference\n for AT&T x86 assembly syntax.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,no_run\n # #![feature(asm)]\n # unsafe { let ptr = \"\".as_ptr();\n asm!(\"lea {}, [{}]\", lateout(reg) _, in(reg) ptr);\n # }\n ```\n Use instead:\n ```rust,no_run\n # #![feature(asm)]\n # unsafe { let ptr = \"\".as_ptr();\n asm!(\"lea ({}), {}\", in(reg) ptr, lateout(reg) _, options(att_syntax));\n # }\n ```\n", + "applicability": null + }, + { + "id": "inline_asm_x86_att_syntax", + "id_span": { + "path": "clippy_lints/src/asm_syntax.rs", + "line": 114 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for usage of AT&T x86 assembly syntax.\n **Why is this bad?** The lint has been enabled to indicate a preference\n for Intel x86 assembly syntax.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,no_run\n # #![feature(asm)]\n # unsafe { let ptr = \"\".as_ptr();\n asm!(\"lea ({}), {}\", in(reg) ptr, lateout(reg) _, options(att_syntax));\n # }\n ```\n Use instead:\n ```rust,no_run\n # #![feature(asm)]\n # unsafe { let ptr = \"\".as_ptr();\n asm!(\"lea {}, [{}]\", lateout(reg) _, in(reg) ptr);\n # }\n ```\n", + "applicability": null + }, + { + "id": "assertions_on_constants", + "id_span": { + "path": "clippy_lints/src/assertions_on_constants.rs", + "line": 23 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `assert!(true)` and `assert!(false)` calls.\n **Why is this bad?** Will be optimized out by the compiler or should probably be replaced by a\n `panic!()` or `unreachable!()`\n\n **Known problems:** None\n\n **Example:**\n ```rust,ignore\n assert!(false)\n assert!(true)\n const B: bool = false;\n assert!(B)\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "assign_op_pattern", + "id_span": { + "path": "clippy_lints/src/assign_ops.rs", + "line": 33 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `a = a op b` or `a = b commutative_op a` patterns.\n\n **Why is this bad?** These can be written as the shorter `a op= b`.\n\n **Known problems:** While forbidden by the spec, `OpAssign` traits may have\n implementations that differ from the regular `Op` impl.\n\n **Example:**\n ```rust\n let mut a = 5;\n let b = 0;\n // ...\n // Bad\n a = a + b;\n\n // Good\n a += b;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "misrefactored_assign_op", + "id_span": { + "path": "clippy_lints/src/assign_ops.rs", + "line": 56 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for `a op= a op b` or `a op= b op a` patterns.\n **Why is this bad?** Most likely these are bugs where one meant to write `a\n op= b`.\n\n **Known problems:** Clippy cannot know for sure if `a op= a op b` should have\n been `a = a op a op b` or `a = a op b`/`a op= b`. Therefore, it suggests both.\n If `a op= a op b` is really the correct behaviour it should be\n written as `a = a op a op b` as it's less confusing.\n\n **Example:**\n ```rust\n let mut a = 5;\n let b = 2;\n // ...\n a += a + b;\n ```\n", + "applicability": { + "is_multi_suggestion": true, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "async_yields_async", + "id_span": { + "path": "clippy_lints/src/async_yields_async.rs", + "line": 36 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for async blocks that yield values of types that can themselves be awaited.\n\n **Why is this bad?** An await is likely missing.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n async fn foo() {}\n\n fn bar() {\n let x = async {\n foo()\n };\n }\n ```\n Use instead:\n ```rust\n async fn foo() {}\n\n fn bar() {\n let x = async {\n foo().await\n };\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "invalid_atomic_ordering", + "id_span": { + "path": "clippy_lints/src/atomic_ordering.rs", + "line": 47 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for usage of invalid atomic ordering in atomic loads/stores/exchanges/updates and\n memory fences.\n\n **Why is this bad?** Using an invalid atomic ordering\n will cause a panic at run-time.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,no_run\n # use std::sync::atomic::{self, AtomicU8, Ordering};\n\n let x = AtomicU8::new(0);\n\n // Bad: `Release` and `AcqRel` cannot be used for `load`.\n let _ = x.load(Ordering::Release);\n let _ = x.load(Ordering::AcqRel);\n\n // Bad: `Acquire` and `AcqRel` cannot be used for `store`.\n x.store(1, Ordering::Acquire);\n x.store(2, Ordering::AcqRel);\n\n // Bad: `Relaxed` cannot be used as a fence's ordering.\n atomic::fence(Ordering::Relaxed);\n atomic::compiler_fence(Ordering::Relaxed);\n\n // Bad: `Release` and `AcqRel` are both always invalid\n // for the failure ordering (the last arg).\n let _ = x.compare_exchange(1, 2, Ordering::SeqCst, Ordering::Release);\n let _ = x.compare_exchange_weak(2, 3, Ordering::AcqRel, Ordering::AcqRel);\n\n // Bad: The failure ordering is not allowed to be\n // stronger than the success order, and `SeqCst` is\n // stronger than `Relaxed`.\n let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |val| Some(val + val));\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "inline_always", + "id_span": { + "path": "clippy_lints/src/attrs.rs", + "line": 65 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for items annotated with `#[inline(always)]`, unless the annotated function is empty or simply panics.\n\n **Why is this bad?** While there are valid uses of this annotation (and once\n you know when to use it, by all means `allow` this lint), it's a common\n newbie-mistake to pepper one's code with it.\n\n As a rule of thumb, before slapping `#[inline(always)]` on a function,\n measure if that additional function call really affects your runtime profile\n sufficiently to make up for the increase in compile time.\n\n **Known problems:** False positives, big time. This lint is meant to be\n deactivated by everyone doing serious performance work. This means having\n done the measurement.\n\n **Example:**\n ```ignore\n #[inline(always)]\n fn not_quite_hot_code(..) { ... }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "useless_attribute", + "id_span": { + "path": "clippy_lints/src/attrs.rs", + "line": 99 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for `extern crate` and `use` items annotated with lint attributes.\n\n This lint permits `#[allow(unused_imports)]`, `#[allow(deprecated)]`,\n `#[allow(unreachable_pub)]`, `#[allow(clippy::wildcard_imports)]` and\n `#[allow(clippy::enum_glob_use)]` on `use` items and `#[allow(unused_imports)]` on\n `extern crate` items with a `#[macro_use]` attribute.\n\n **Why is this bad?** Lint attributes have no effect on crate imports. Most\n likely a `!` was forgotten.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n // Bad\n #[deny(dead_code)]\n extern crate foo;\n #[forbid(dead_code)]\n use foo::bar;\n\n // Ok\n #[allow(unused_imports)]\n use foo::baz;\n #[allow(unused_imports)]\n #[macro_use]\n extern crate baz;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "deprecated_semver", + "id_span": { + "path": "clippy_lints/src/attrs.rs", + "line": 118 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for `#[deprecated]` annotations with a `since` field that is not a valid semantic version.\n\n **Why is this bad?** For checking the version of the deprecation, it must be\n a valid semver. Failing that, the contained information is useless.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n #[deprecated(since = \"forever\")]\n fn something_else() { /* ... */ }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "empty_line_after_outer_attr", + "id_span": { + "path": "clippy_lints/src/attrs.rs", + "line": 153 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Checks for empty lines after outer attributes\n **Why is this bad?**\n Most likely the attribute was meant to be an inner attribute using a '!'.\n If it was meant to be an outer attribute, then the following item\n should not be separated by empty lines.\n\n **Known problems:** Can cause false positives.\n\n From the clippy side it's difficult to detect empty lines between an attributes and the\n following item because empty lines and comments are not part of the AST. The parsing\n currently works for basic cases but is not perfect.\n\n **Example:**\n ```rust\n // Good (as inner attribute)\n #![allow(dead_code)]\n\n fn this_is_fine() { }\n\n // Bad\n #[allow(dead_code)]\n\n fn not_quite_good_code() { }\n\n // Good (as outer attribute)\n #[allow(dead_code)]\n fn this_is_fine_too() { }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "blanket_clippy_restriction_lints", + "id_span": { + "path": "clippy_lints/src/attrs.rs", + "line": 176 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category.\n **Why is this bad?** Restriction lints sometimes are in contrast with other lints or even go against idiomatic rust.\n These lints should only be enabled on a lint-by-lint basis and with careful consideration.\n\n **Known problems:** None.\n\n **Example:**\n Bad:\n ```rust\n #![deny(clippy::restriction)]\n ```\n\n Good:\n ```rust\n #![deny(clippy::as_conversions)]\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "deprecated_cfg_attr", + "id_span": { + "path": "clippy_lints/src/attrs.rs", + "line": 205 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it with `#[rustfmt::skip]`.\n\n **Why is this bad?** Since tool_attributes ([rust-lang/rust#44690](https://github.com/rust-lang/rust/issues/44690))\n are stable now, they should be used instead of the old `cfg_attr(rustfmt)` attributes.\n\n **Known problems:** This lint doesn't detect crate level inner attributes, because they get\n processed before the PreExpansionPass lints get executed. See\n [#3123](https://github.com/rust-lang/rust-clippy/pull/3123#issuecomment-422321765)\n\n **Example:**\n\n Bad:\n ```rust\n #[cfg_attr(rustfmt, rustfmt_skip)]\n fn main() { }\n ```\n\n Good:\n ```rust\n #[rustfmt::skip]\n fn main() { }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "mismatched_target_os", + "id_span": { + "path": "clippy_lints/src/attrs.rs", + "line": 238 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for cfg attributes having operating systems used in target family position.\n **Why is this bad?** The configuration option will not be recognised and the related item will not be included\n by the conditional compilation engine.\n\n **Known problems:** None.\n\n **Example:**\n\n Bad:\n ```rust\n #[cfg(linux)]\n fn conditional() { }\n ```\n\n Good:\n ```rust\n #[cfg(target_os = \"linux\")]\n fn conditional() { }\n ```\n\n Or:\n ```rust\n #[cfg(unix)]\n fn conditional() { }\n ```\n Check the [Rust Reference](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os) for more details.\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "await_holding_lock", + "id_span": { + "path": "clippy_lints/src/await_holding_invalid.rs", + "line": 47 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for calls to await while holding a non-async-aware MutexGuard.\n\n **Why is this bad?** The Mutex types found in std::sync and parking_lot\n are not designed to operate in an async context across await points.\n\n There are two potential solutions. One is to use an asynx-aware Mutex\n type. Many asynchronous foundation crates provide such a Mutex type. The\n other solution is to ensure the mutex is unlocked before calling await,\n either by introducing a scope or an explicit call to Drop::drop.\n\n **Known problems:** Will report false positive for explicitly dropped guards ([#6446](https://github.com/rust-lang/rust-clippy/issues/6446)).\n\n **Example:**\n\n ```rust,ignore\n use std::sync::Mutex;\n\n async fn foo(x: &Mutex) {\n let guard = x.lock().unwrap();\n *guard += 1;\n bar.await;\n }\n ```\n\n Use instead:\n ```rust,ignore\n use std::sync::Mutex;\n\n async fn foo(x: &Mutex) {\n {\n let guard = x.lock().unwrap();\n *guard += 1;\n }\n bar.await;\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "await_holding_refcell_ref", + "id_span": { + "path": "clippy_lints/src/await_holding_invalid.rs", + "line": 86 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for calls to await while holding a `RefCell` `Ref` or `RefMut`.\n\n **Why is this bad?** `RefCell` refs only check for exclusive mutable access\n at runtime. Holding onto a `RefCell` ref across an `await` suspension point\n risks panics from a mutable ref shared while other refs are outstanding.\n\n **Known problems:** Will report false positive for explicitly dropped refs ([#6353](https://github.com/rust-lang/rust-clippy/issues/6353)).\n\n **Example:**\n\n ```rust,ignore\n use std::cell::RefCell;\n\n async fn foo(x: &RefCell) {\n let mut y = x.borrow_mut();\n *y += 1;\n bar.await;\n }\n ```\n\n Use instead:\n ```rust,ignore\n use std::cell::RefCell;\n\n async fn foo(x: &RefCell) {\n {\n let mut y = x.borrow_mut();\n *y += 1;\n }\n bar.await;\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "bad_bit_mask", + "id_span": { + "path": "clippy_lints/src/bit_mask.rs", + "line": 44 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for incompatible bit masks in comparisons.\n The formula for detecting if an expression of the type `_ m\n c` (where `` is one of {`&`, `|`} and `` is one of\n {`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following\n table:\n\n |Comparison |Bit Op|Example |is always|Formula |\n |------------|------|------------|---------|----------------------|\n |`==` or `!=`| `&` |`x & 2 == 3`|`false` |`c & m != c` |\n |`<` or `>=`| `&` |`x & 2 < 3` |`true` |`m < c` |\n |`>` or `<=`| `&` |`x & 1 > 1` |`false` |`m <= c` |\n |`==` or `!=`| `|` |`x | 1 == 0`|`false` |`c | m != c` |\n |`<` or `>=`| `|` |`x | 1 < 1` |`false` |`m >= c` |\n |`<=` or `>` | `|` |`x | 1 > 0` |`true` |`m > c` |\n\n **Why is this bad?** If the bits that the comparison cares about are always\n set to zero or one by the bit mask, the comparison is constant `true` or\n `false` (depending on mask, compared value, and operators).\n\n So the code is actively misleading, and the only reason someone would write\n this intentionally is to win an underhanded Rust contest or create a\n test-case for this lint.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = 1;\n if (x & 1 == 2) { }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "ineffective_bit_mask", + "id_span": { + "path": "clippy_lints/src/bit_mask.rs", + "line": 73 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for bit masks in comparisons which can be removed without changing the outcome. The basic structure can be seen in the\n following table:\n\n |Comparison| Bit Op |Example |equals |\n |----------|---------|-----------|-------|\n |`>` / `<=`|`|` / `^`|`x | 2 > 3`|`x > 3`|\n |`<` / `>=`|`|` / `^`|`x ^ 1 < 4`|`x < 4`|\n\n **Why is this bad?** Not equally evil as [`bad_bit_mask`](#bad_bit_mask),\n but still a bit misleading, because the bit mask is ineffective.\n\n **Known problems:** False negatives: This lint will only match instances\n where we have figured out the math (which is for a power-of-two compared\n value). This means things like `x | 1 >= 7` (which would be better written\n as `x >= 6`) will not be reported (but bit masks like this are fairly\n uncommon).\n\n **Example:**\n ```rust\n # let x = 1;\n if (x | 1 > 3) { }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "verbose_bit_mask", + "id_span": { + "path": "clippy_lints/src/bit_mask.rs", + "line": 92 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for bit masks that can be replaced by a call to `trailing_zeros`\n\n **Why is this bad?** `x.trailing_zeros() > 4` is much clearer than `x & 15\n == 0`\n\n **Known problems:** llvm generates better code for `x & 15 == 0` on x86\n\n **Example:**\n ```rust\n # let x = 1;\n if x & 0b1111 == 0 { }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "blacklisted_name", + "id_span": { + "path": "clippy_lints/src/blacklisted_name.rs", + "line": 20 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for usage of blacklisted names for variables, such as `foo`.\n\n **Why is this bad?** These names are usually placeholder names and should be\n avoided.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let foo = 3.14;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "blocks_in_if_conditions", + "id_span": { + "path": "clippy_lints/src/blocks_in_if_conditions.rs", + "line": 42 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `if` conditions that use blocks containing an expression, statements or conditions that use closures with blocks.\n\n **Why is this bad?** Style, using blocks in the condition makes it hard to read.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n // Bad\n if { true } { /* ... */ }\n\n // Good\n if true { /* ... */ }\n ```\n\n // or\n\n ```rust\n # fn somefunc() -> bool { true };\n // Bad\n if { let x = somefunc(); x } { /* ... */ }\n\n // Good\n let res = { let x = somefunc(); x };\n if res { /* ... */ }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "nonminimal_bool", + "id_span": { + "path": "clippy_lints/src/booleans.rs", + "line": 31 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for boolean expressions that can be written more concisely.\n\n **Why is this bad?** Readability of boolean expressions suffers from\n unnecessary duplication.\n\n **Known problems:** Ignores short circuiting behavior of `||` and\n `&&`. Ignores `|`, `&` and `^`.\n\n **Example:**\n ```ignore\n if a && true // should be: if a\n if !(a == b) // should be: if a != b\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "logic_bug", + "id_span": { + "path": "clippy_lints/src/booleans.rs", + "line": 49 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for boolean expressions that contain terminals that can be eliminated.\n\n **Why is this bad?** This is most likely a logic bug.\n\n **Known problems:** Ignores short circuiting behavior.\n\n **Example:**\n ```ignore\n if a && b || a { ... }\n ```\n The `b` is unnecessary, the expression is equivalent to `if a`.\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "naive_bytecount", + "id_span": { + "path": "clippy_lints/src/bytecount.rs", + "line": 30 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for naive byte counts\n **Why is this bad?** The [`bytecount`](https://crates.io/crates/bytecount)\n crate has methods to count your bytes faster, especially for large slices.\n\n **Known problems:** If you have predominantly small slices, the\n `bytecount::count(..)` method may actually be slower. However, if you can\n ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be\n faster in those cases.\n\n **Example:**\n\n ```rust\n # let vec = vec![1_u8];\n &vec.iter().filter(|x| **x == 0u8).count(); // use bytecount::count instead\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "cargo_common_metadata", + "id_span": { + "path": "clippy_lints/src/cargo_common_metadata.rs", + "line": 49 + }, + "group": "clippy::cargo", + "docs": " **What it does:** Checks to see if all common metadata is defined in `Cargo.toml`. See: https://rust-lang-nursery.github.io/api-guidelines/documentation.html#cargotoml-includes-all-common-metadata-c-metadata\n\n **Why is this bad?** It will be more difficult for users to discover the\n purpose of the crate, and key information related to it.\n\n **Known problems:** None.\n\n **Example:**\n ```toml\n # This `Cargo.toml` is missing an authors field:\n [package]\n name = \"clippy\"\n version = \"0.0.212\"\n description = \"A bunch of helpful lints to avoid common pitfalls in Rust\"\n repository = \"https://github.com/rust-lang/rust-clippy\"\n readme = \"README.md\"\n license = \"MIT OR Apache-2.0\"\n keywords = [\"clippy\", \"lint\", \"plugin\"]\n categories = [\"development-tools\", \"development-tools::cargo-plugins\"]\n ```\n\n Should include an authors field like:\n\n ```toml\n # This `Cargo.toml` includes all common metadata\n [package]\n name = \"clippy\"\n version = \"0.0.212\"\n authors = [\"Someone \"]\n description = \"A bunch of helpful lints to avoid common pitfalls in Rust\"\n repository = \"https://github.com/rust-lang/rust-clippy\"\n readme = \"README.md\"\n license = \"MIT OR Apache-2.0\"\n keywords = [\"clippy\", \"lint\", \"plugin\"]\n categories = [\"development-tools\", \"development-tools::cargo-plugins\"]\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "case_sensitive_file_extension_comparisons", + "id_span": { + "path": "clippy_lints/src/case_sensitive_file_extension_comparisons.rs", + "line": 34 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for calls to `ends_with` with possible file extensions\n and suggests to use a case-insensitive approach instead.\n\n **Why is this bad?**\n `ends_with` is case-sensitive and may not detect files with a valid extension.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n fn is_rust_file(filename: &str) -> bool {\n filename.ends_with(\".rs\")\n }\n ```\n Use instead:\n ```rust\n fn is_rust_file(filename: &str) -> bool {\n filename.rsplit('.').next().map(|ext| ext.eq_ignore_ascii_case(\"rs\")) == Some(true)\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "checked_conversions", + "id_span": { + "path": "clippy_lints/src/checked_conversions.rs", + "line": 40 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for explicit bounds checking when casting.\n **Why is this bad?** Reduces the readability of statements & is error prone.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let foo: u32 = 5;\n # let _ =\n foo <= i32::MAX as u32\n # ;\n ```\n\n Could be written:\n\n ```rust\n # use std::convert::TryFrom;\n # let foo = 1;\n # let _ =\n i32::try_from(foo).is_ok()\n # ;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "cognitive_complexity", + "id_span": { + "path": "clippy_lints/src/cognitive_complexity.rs", + "line": 24 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Checks for methods with high cognitive complexity.\n **Why is this bad?** Methods of high cognitive complexity tend to be hard to\n both read and maintain. Also LLVM will tend to optimize small methods better.\n\n **Known problems:** Sometimes it's hard to find a way to reduce the\n complexity.\n\n **Example:** No. You'll see it when you get the warning.\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "collapsible_if", + "id_span": { + "path": "clippy_lints/src/collapsible_if.rs", + "line": 50 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for nested `if` statements which can be collapsed by `&&`-combining their conditions.\n\n **Why is this bad?** Each `if`-statement adds one level of nesting, which\n makes code look more complex than it really is.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n if x {\n if y {\n …\n }\n }\n\n ```\n\n Should be written:\n\n ```rust.ignore\n if x && y {\n …\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "collapsible_else_if", + "id_span": { + "path": "clippy_lints/src/collapsible_if.rs", + "line": 85 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for collapsible `else { if ... }` expressions that can be collapsed to `else if ...`.\n\n **Why is this bad?** Each `if`-statement adds one level of nesting, which\n makes code look more complex than it really is.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n\n if x {\n …\n } else {\n if y {\n …\n }\n }\n ```\n\n Should be written:\n\n ```rust.ignore\n if x {\n …\n } else if y {\n …\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "collapsible_match", + "id_span": { + "path": "clippy_lints/src/collapsible_match.rs", + "line": 44 + }, + "group": "clippy::style", + "docs": " **What it does:** Finds nested `match` or `if let` expressions where the patterns may be \"collapsed\" together without adding any branches.\n\n Note that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only\n cases where merging would most likely make the code more readable.\n\n **Why is this bad?** It is unnecessarily verbose and complex.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n fn func(opt: Option>) {\n let n = match opt {\n Some(n) => match n {\n Ok(n) => n,\n _ => return,\n }\n None => return,\n };\n }\n ```\n Use instead:\n ```rust\n fn func(opt: Option>) {\n let n = match opt {\n Some(Ok(n)) => n,\n _ => return,\n };\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "comparison_chain", + "id_span": { + "path": "clippy_lints/src/comparison_chain.rs", + "line": 49 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks comparison chains written with `if` that can be rewritten with `match` and `cmp`.\n\n **Why is this bad?** `if` is not guaranteed to be exhaustive and conditionals can get\n repetitive\n\n **Known problems:** The match statement may be slower due to the compiler\n not inlining the call to cmp. See issue [#5354](https://github.com/rust-lang/rust-clippy/issues/5354)\n\n **Example:**\n ```rust,ignore\n # fn a() {}\n # fn b() {}\n # fn c() {}\n fn f(x: u8, y: u8) {\n if x > y {\n a()\n } else if x < y {\n b()\n } else {\n c()\n }\n }\n ```\n\n Could be written:\n\n ```rust,ignore\n use std::cmp::Ordering;\n # fn a() {}\n # fn b() {}\n # fn c() {}\n fn f(x: u8, y: u8) {\n match x.cmp(&y) {\n Ordering::Greater => a(),\n Ordering::Less => b(),\n Ordering::Equal => c()\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "ifs_same_cond", + "id_span": { + "path": "clippy_lints/src/copies.rs", + "line": 33 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for consecutive `if`s with the same condition.\n **Why is this bad?** This is probably a copy & paste error.\n\n **Known problems:** Hopefully none.\n\n **Example:**\n ```ignore\n if a == b {\n …\n } else if a == b {\n …\n }\n ```\n\n Note that this lint ignores all conditions with a function call as it could\n have side effects:\n\n ```ignore\n if foo() {\n …\n } else if foo() { // not linted\n …\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "same_functions_in_if_condition", + "id_span": { + "path": "clippy_lints/src/copies.rs", + "line": 80 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for consecutive `if`s with the same function call.\n **Why is this bad?** This is probably a copy & paste error.\n Despite the fact that function can have side effects and `if` works as\n intended, such an approach is implicit and can be considered a \"code smell\".\n\n **Known problems:** Hopefully none.\n\n **Example:**\n ```ignore\n if foo() == bar {\n …\n } else if foo() == bar {\n …\n }\n ```\n\n This probably should be:\n ```ignore\n if foo() == bar {\n …\n } else if foo() == baz {\n …\n }\n ```\n\n or if the original code was not a typo and called function mutates a state,\n consider move the mutation out of the `if` condition to avoid similarity to\n a copy & paste error:\n\n ```ignore\n let first = foo();\n if first == bar {\n …\n } else {\n let second = foo();\n if second == bar {\n …\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "if_same_then_else", + "id_span": { + "path": "clippy_lints/src/copies.rs", + "line": 101 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for `if/else` with the same body as the *then* part and the *else* part.\n\n **Why is this bad?** This is probably a copy & paste error.\n\n **Known problems:** Hopefully none.\n\n **Example:**\n ```ignore\n let foo = if … {\n 42\n } else {\n 42\n };\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "copy_iterator", + "id_span": { + "path": "clippy_lints/src/copy_iterator.rs", + "line": 27 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for types that implement `Copy` as well as `Iterator`.\n\n **Why is this bad?** Implicit copies can be confusing when working with\n iterator combinators.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n #[derive(Copy, Clone)]\n struct Countdown(u8);\n\n impl Iterator for Countdown {\n // ...\n }\n\n let a: Vec<_> = my_iterator.take(1).collect();\n let b: Vec<_> = my_iterator.collect();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "create_dir", + "id_span": { + "path": "clippy_lints/src/create_dir.rs", + "line": 24 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks usage of `std::fs::create_dir` and suggest using `std::fs::create_dir_all` instead.\n **Why is this bad?** Sometimes `std::fs::create_dir` is mistakenly chosen over `std::fs::create_dir_all`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n std::fs::create_dir(\"foo\");\n ```\n Use instead:\n ```rust\n std::fs::create_dir_all(\"foo\");\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "dbg_macro", + "id_span": { + "path": "clippy_lints/src/dbg_macro.rs", + "line": 25 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for usage of dbg!() macro.\n **Why is this bad?** `dbg!` macro is intended as a debugging tool. It\n should not be in version control.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n // Bad\n dbg!(true)\n\n // Good\n true\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "default_trait_access", + "id_span": { + "path": "clippy_lints/src/default.rs", + "line": 33 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for literal calls to `Default::default()`.\n **Why is this bad?** It's more clear to the reader to use the name of the type whose default is\n being gotten than the generic `Default`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n let s: String = Default::default();\n\n // Good\n let s = String::default();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "field_reassign_with_default", + "id_span": { + "path": "clippy_lints/src/default.rs", + "line": 63 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for immediate reassignment of fields initialized with Default::default().\n\n **Why is this bad?**It's more idiomatic to use the [functional update syntax](https://doc.rust-lang.org/reference/expressions/struct-expr.html#functional-update-syntax).\n\n **Known problems:** Assignments to patterns that are of tuple type are not linted.\n\n **Example:**\n Bad:\n ```\n # #[derive(Default)]\n # struct A { i: i32 }\n let mut a: A = Default::default();\n a.i = 42;\n ```\n Use instead:\n ```\n # #[derive(Default)]\n # struct A { i: i32 }\n let a = A {\n i: 42,\n .. Default::default()\n };\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "default_numeric_fallback", + "id_span": { + "path": "clippy_lints/src/default_numeric_fallback.rs", + "line": 44 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for usage of unconstrained numeric literals which may cause default numeric fallback in type inference.\n\n Default numeric fallback means that if numeric types have not yet been bound to concrete\n types at the end of type inference, then integer type is bound to `i32`, and similarly\n floating type is bound to `f64`.\n\n See [RFC0212](https://github.com/rust-lang/rfcs/blob/master/text/0212-restore-int-fallback.md) for more information about the fallback.\n\n **Why is this bad?** For those who are very careful about types, default numeric fallback\n can be a pitfall that cause unexpected runtime behavior.\n\n **Known problems:** This lint can only be allowed at the function level or above.\n\n **Example:**\n ```rust\n let i = 10;\n let f = 1.23;\n ```\n\n Use instead:\n ```rust\n let i = 10i32;\n let f = 1.23f64;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "explicit_deref_methods", + "id_span": { + "path": "clippy_lints/src/dereference.rs", + "line": 32 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for explicit `deref()` or `deref_mut()` method calls.\n **Why is this bad?** Dereferencing by `&*x` or `&mut *x` is clearer and more concise,\n when not part of a method chain.\n\n **Example:**\n ```rust\n use std::ops::Deref;\n let a: &mut String = &mut String::from(\"foo\");\n let b: &str = a.deref();\n ```\n Could be written as:\n ```rust\n let a: &mut String = &mut String::from(\"foo\");\n let b = &*a;\n ```\n\n This lint excludes\n ```rust,ignore\n let _ = d.unwrap().deref();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "derive_hash_xor_eq", + "id_span": { + "path": "clippy_lints/src/derive.rs", + "line": 42 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for deriving `Hash` but implementing `PartialEq` explicitly or vice versa.\n\n **Why is this bad?** The implementation of these traits must agree (for\n example for use with `HashMap`) so it’s probably a bad idea to use a\n default-generated `Hash` implementation with an explicitly defined\n `PartialEq`. In particular, the following must hold for any type:\n\n ```text\n k1 == k2 ⇒ hash(k1) == hash(k2)\n ```\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n #[derive(Hash)]\n struct Foo;\n\n impl PartialEq for Foo {\n ...\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "derive_ord_xor_partial_ord", + "id_span": { + "path": "clippy_lints/src/derive.rs", + "line": 93 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for deriving `Ord` but implementing `PartialOrd` explicitly or vice versa.\n\n **Why is this bad?** The implementation of these traits must agree (for\n example for use with `sort`) so it’s probably a bad idea to use a\n default-generated `Ord` implementation with an explicitly defined\n `PartialOrd`. In particular, the following must hold for any type\n implementing `Ord`:\n\n ```text\n k1.cmp(&k2) == k1.partial_cmp(&k2).unwrap()\n ```\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,ignore\n #[derive(Ord, PartialEq, Eq)]\n struct Foo;\n\n impl PartialOrd for Foo {\n ...\n }\n ```\n Use instead:\n ```rust,ignore\n #[derive(PartialEq, Eq)]\n struct Foo;\n\n impl PartialOrd for Foo {\n fn partial_cmp(&self, other: &Foo) -> Option {\n Some(self.cmp(other))\n }\n }\n\n impl Ord for Foo {\n ...\n }\n ```\n or, if you don't need a custom ordering:\n ```rust,ignore\n #[derive(Ord, PartialOrd, PartialEq, Eq)]\n struct Foo;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "expl_impl_clone_on_copy", + "id_span": { + "path": "clippy_lints/src/derive.rs", + "line": 119 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for explicit `Clone` implementations for `Copy` types.\n\n **Why is this bad?** To avoid surprising behaviour, these traits should\n agree and the behaviour of `Copy` cannot be overridden. In almost all\n situations a `Copy` type should have a `Clone` implementation that does\n nothing more than copy the object, which is what `#[derive(Copy, Clone)]`\n gets you.\n\n **Known problems:** Bounds of generic types are sometimes wrong: https://github.com/rust-lang/rust/issues/26925\n\n **Example:**\n ```rust,ignore\n #[derive(Copy)]\n struct Foo;\n\n impl Clone for Foo {\n // ..\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "unsafe_derive_deserialize", + "id_span": { + "path": "clippy_lints/src/derive.rs", + "line": 153 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for deriving `serde::Deserialize` on a type that has methods using `unsafe`.\n\n **Why is this bad?** Deriving `serde::Deserialize` will create a constructor\n that may violate invariants hold by another constructor.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,ignore\n use serde::Deserialize;\n\n #[derive(Deserialize)]\n pub struct Foo {\n // ..\n }\n\n impl Foo {\n pub fn new() -> Self {\n // setup here ..\n }\n\n pub unsafe fn parts() -> (&str, &str) {\n // assumes invariants hold\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "disallowed_method", + "id_span": { + "path": "clippy_lints/src/disallowed_method.rs", + "line": 46 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Denies the configured methods and functions in clippy.toml\n **Why is this bad?** Some methods are undesirable in certain contexts,\n and it's beneficial to lint for them as needed.\n\n **Known problems:** Currently, you must write each function as a\n fully-qualified path. This lint doesn't support aliases or reexported\n names; be aware that many types in `std` are actually reexports.\n\n For example, if you want to disallow `Duration::as_secs`, your clippy.toml\n configuration would look like\n `disallowed-methods = [\"core::time::Duration::as_secs\"]` and not\n `disallowed-methods = [\"std::time::Duration::as_secs\"]` as you might expect.\n\n **Example:**\n\n An example clippy.toml configuration:\n ```toml\n # clippy.toml\n disallowed-methods = [\"alloc::vec::Vec::leak\", \"std::time::Instant::now\"]\n ```\n\n ```rust,ignore\n // Example code where clippy issues a warning\n let xs = vec![1, 2, 3, 4];\n xs.leak(); // Vec::leak is disallowed in the config.\n\n let _now = Instant::now(); // Instant::now is disallowed in the config.\n ```\n\n Use instead:\n ```rust,ignore\n // Example code which does not raise clippy warning\n let mut xs = Vec::new(); // Vec::new is _not_ disallowed in the config.\n xs.push(123); // Vec::push is _not_ disallowed in the config.\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "doc_markdown", + "id_span": { + "path": "clippy_lints/src/doc.rs", + "line": 63 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for the presence of `_`, `::` or camel-case words outside ticks in documentation.\n\n **Why is this bad?** *Rustdoc* supports markdown formatting, `_`, `::` and\n camel-case probably indicates some code which should be included between\n ticks. `_` can also be used for emphasis in markdown, this lint tries to\n consider that.\n\n **Known problems:** Lots of bad docs won’t be fixed, what the lint checks\n for is limited, and there are still false positives.\n\n In addition, when writing documentation comments, including `[]` brackets\n inside a link text would trip the parser. Therfore, documenting link with\n `[`SmallVec<[T; INLINE_CAPACITY]>`]` and then [`SmallVec<[T; INLINE_CAPACITY]>`]: SmallVec\n would fail.\n\n **Examples:**\n ```rust\n /// Do something with the foo_bar parameter. See also\n /// that::other::module::foo.\n // ^ `foo_bar` and `that::other::module::foo` should be ticked.\n fn doit(foo_bar: usize) {}\n ```\n\n ```rust\n // Link text with `[]` brackets should be written as following:\n /// Consume the array and return the inner\n /// [`SmallVec<[T; INLINE_CAPACITY]>`][SmallVec].\n /// [SmallVec]: SmallVec\n fn main() {}\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "missing_safety_doc", + "id_span": { + "path": "clippy_lints/src/doc.rs", + "line": 97 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for the doc comments of publicly visible unsafe functions and warns if there is no `# Safety` section.\n\n **Why is this bad?** Unsafe functions should document their safety\n preconditions, so that users can be sure they are using them safely.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n# type Universe = ();\n /// This function should really be documented\n pub unsafe fn start_apocalypse(u: &mut Universe) {\n unimplemented!();\n }\n ```\n\n At least write a line about safety:\n\n ```rust\n# type Universe = ();\n /// # Safety\n ///\n /// This function should not be called before the horsemen are ready.\n pub unsafe fn start_apocalypse(u: &mut Universe) {\n unimplemented!();\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "missing_errors_doc", + "id_span": { + "path": "clippy_lints/src/doc.rs", + "line": 126 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks the doc comments of publicly visible functions that return a `Result` type and warns if there is no `# Errors` section.\n\n **Why is this bad?** Documenting the type of errors that can be returned from a\n function can help callers write code to handle the errors appropriately.\n\n **Known problems:** None.\n\n **Examples:**\n\n Since the following function returns a `Result` it has an `# Errors` section in\n its doc comment:\n\n ```rust\n# use std::io;\n /// # Errors\n ///\n /// Will return `Err` if `filename` does not exist or the user does not have\n /// permission to read it.\n pub fn read(filename: String) -> io::Result {\n unimplemented!();\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "missing_panics_doc", + "id_span": { + "path": "clippy_lints/src/doc.rs", + "line": 157 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks the doc comments of publicly visible functions that may panic and warns if there is no `# Panics` section.\n\n **Why is this bad?** Documenting the scenarios in which panicking occurs\n can help callers who do not want to panic to avoid those situations.\n\n **Known problems:** None.\n\n **Examples:**\n\n Since the following function may panic it has a `# Panics` section in\n its doc comment:\n\n ```rust\n /// # Panics\n ///\n /// Will panic if y is 0\n pub fn divide_by(x: i32, y: i32) -> i32 {\n if y == 0 {\n panic!(\"Cannot divide by 0\")\n } else {\n x / y\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "needless_doctest_main", + "id_span": { + "path": "clippy_lints/src/doc.rs", + "line": 185 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `fn main() { .. }` in doctests\n **Why is this bad?** The test can be shorter (and likely more readable)\n if the `fn main()` is left implicit.\n\n **Known problems:** None.\n\n **Examples:**\n ``````rust\n /// An example of a doctest with a `main()` function\n ///\n /// # Examples\n ///\n /// ```\n /// fn main() {\n /// // this needs not be in an `fn`\n /// }\n /// ```\n fn needless_main() {\n unimplemented!();\n }\n ``````\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "double_comparisons", + "id_span": { + "path": "clippy_lints/src/double_comparison.rs", + "line": 33 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for double comparisons that could be simplified to a single expression.\n\n **Why is this bad?** Readability.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = 1;\n # let y = 2;\n if x == y || x < y {}\n ```\n\n Could be written as:\n\n ```rust\n # let x = 1;\n # let y = 2;\n if x <= y {}\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "double_parens", + "id_span": { + "path": "clippy_lints/src/double_parens.rs", + "line": 35 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for unnecessary double parentheses.\n **Why is this bad?** This makes code harder to read and might indicate a\n mistake.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n fn simple_double_parens() -> i32 {\n ((0))\n }\n\n // Good\n fn simple_no_parens() -> i32 {\n 0\n }\n\n // or\n\n # fn foo(bar: usize) {}\n // Bad\n foo((0));\n\n // Good\n foo(0);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "drop_ref", + "id_span": { + "path": "clippy_lints/src/drop_forget_ref.rs", + "line": 26 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for calls to `std::mem::drop` with a reference instead of an owned value.\n\n **Why is this bad?** Calling `drop` on a reference will only drop the\n reference itself, which is a no-op. It will not call the `drop` method (from\n the `Drop` trait implementation) on the underlying referenced value, which\n is likely what was intended.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n let mut lock_guard = mutex.lock();\n std::mem::drop(&lock_guard) // Should have been drop(lock_guard), mutex\n // still locked\n operation_that_requires_mutex_to_be_unlocked();\n ```\n", + "applicability": null + }, + { + "id": "forget_ref", + "id_span": { + "path": "clippy_lints/src/drop_forget_ref.rs", + "line": 47 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for calls to `std::mem::forget` with a reference instead of an owned value.\n\n **Why is this bad?** Calling `forget` on a reference will only forget the\n reference itself, which is a no-op. It will not forget the underlying\n referenced\n value, which is likely what was intended.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = Box::new(1);\n std::mem::forget(&x) // Should have been forget(x), x will still be dropped\n ```\n", + "applicability": null + }, + { + "id": "drop_copy", + "id_span": { + "path": "clippy_lints/src/drop_forget_ref.rs", + "line": 68 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for calls to `std::mem::drop` with a value that derives the Copy trait\n\n **Why is this bad?** Calling `std::mem::drop` [does nothing for types that\n implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html), since the\n value will be copied and moved into the function on invocation.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x: i32 = 42; // i32 implements Copy\n std::mem::drop(x) // A copy of x is passed to the function, leaving the\n // original unaffected\n ```\n", + "applicability": null + }, + { + "id": "forget_copy", + "id_span": { + "path": "clippy_lints/src/drop_forget_ref.rs", + "line": 95 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for calls to `std::mem::forget` with a value that derives the Copy trait\n\n **Why is this bad?** Calling `std::mem::forget` [does nothing for types that\n implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html) since the\n value will be copied and moved into the function on invocation.\n\n An alternative, but also valid, explanation is that Copy types do not\n implement\n the Drop trait, which means they have no destructors. Without a destructor,\n there\n is nothing for `std::mem::forget` to ignore.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x: i32 = 42; // i32 implements Copy\n std::mem::forget(x) // A copy of x is passed to the function, leaving the\n // original unaffected\n ```\n", + "applicability": null + }, + { + "id": "duration_subsec", + "id_span": { + "path": "clippy_lints/src/duration_subsec.rs", + "line": 34 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for calculation of subsecond microseconds or milliseconds from other `Duration` methods.\n\n **Why is this bad?** It's more concise to call `Duration::subsec_micros()` or\n `Duration::subsec_millis()` than to calculate them.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::time::Duration;\n let dur = Duration::new(5, 0);\n\n // Bad\n let _micros = dur.subsec_nanos() / 1_000;\n let _millis = dur.subsec_nanos() / 1_000_000;\n\n // Good\n let _micros = dur.subsec_micros();\n let _millis = dur.subsec_millis();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "else_if_without_else", + "id_span": { + "path": "clippy_lints/src/else_if_without_else.rs", + "line": 44 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for usage of if expressions with an `else if` branch, but without a final `else` branch.\n\n **Why is this bad?** Some coding guidelines require this (e.g., MISRA-C:2004 Rule 14.10).\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn a() {}\n # fn b() {}\n # let x: i32 = 1;\n if x.is_positive() {\n a();\n } else if x.is_negative() {\n b();\n }\n ```\n\n Could be written:\n\n ```rust\n # fn a() {}\n # fn b() {}\n # let x: i32 = 1;\n if x.is_positive() {\n a();\n } else if x.is_negative() {\n b();\n } else {\n // We don't care about zero.\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "empty_enum", + "id_span": { + "path": "clippy_lints/src/empty_enum.rs", + "line": 38 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for `enum`s with no variants.\n As of this writing, the `never_type` is still a\n nightly-only experimental API. Therefore, this lint is only triggered\n if the `never_type` is enabled.\n\n **Why is this bad?** If you want to introduce a type which\n can't be instantiated, you should use `!` (the primitive type \"never\"),\n or a wrapper around it, because `!` has more extensive\n compiler support (type inference, etc...) and wrappers\n around it are the conventional way to define an uninhabited type.\n For further information visit [never type documentation](https://doc.rust-lang.org/std/primitive.never.html)\n\n\n **Known problems:** None.\n\n **Example:**\n\n Bad:\n ```rust\n enum Test {}\n ```\n\n Good:\n ```rust\n #![feature(never_type)]\n\n struct Test(!);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "map_entry", + "id_span": { + "path": "clippy_lints/src/entry.rs", + "line": 48 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for uses of `contains_key` + `insert` on `HashMap` or `BTreeMap`.\n\n **Why is this bad?** Using `entry` is more efficient.\n\n **Known problems:** Some false negatives, eg.:\n ```rust\n # use std::collections::HashMap;\n # let mut map = HashMap::new();\n # let v = 1;\n # let k = 1;\n if !map.contains_key(&k) {\n map.insert(k.clone(), v);\n }\n ```\n\n **Example:**\n ```rust\n # use std::collections::HashMap;\n # let mut map = HashMap::new();\n # let k = 1;\n # let v = 1;\n if !map.contains_key(&k) {\n map.insert(k, v);\n }\n ```\n can both be rewritten as:\n ```rust\n # use std::collections::HashMap;\n # let mut map = HashMap::new();\n # let k = 1;\n # let v = 1;\n map.entry(k).or_insert(v);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "enum_clike_unportable_variant", + "id_span": { + "path": "clippy_lints/src/enum_clike.rs", + "line": 31 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for C-like enumerations that are `repr(isize/usize)` and have values that don't fit into an `i32`.\n\n **Why is this bad?** This will truncate the variant value on 32 bit\n architectures, but works fine on 64 bit.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # #[cfg(target_pointer_width = \"64\")]\n #[repr(usize)]\n enum NonPortable {\n X = 0x1_0000_0000,\n Y = 0,\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "enum_variant_names", + "id_span": { + "path": "clippy_lints/src/enum_variants.rs", + "line": 36 + }, + "group": "clippy::style", + "docs": " **What it does:** Detects enumeration variants that are prefixed or suffixed by the same characters.\n\n **Why is this bad?** Enumeration variant names should specify their variant,\n not repeat the enumeration name.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n enum Cake {\n BlackForestCake,\n HummingbirdCake,\n BattenbergCake,\n }\n ```\n Could be written as:\n ```rust\n enum Cake {\n BlackForest,\n Hummingbird,\n Battenberg,\n }\n ```\n", + "applicability": null + }, + { + "id": "pub_enum_variant_names", + "id_span": { + "path": "clippy_lints/src/enum_variants.rs", + "line": 66 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Detects public enumeration variants that are prefixed or suffixed by the same characters.\n\n **Why is this bad?** Public enumeration variant names should specify their variant,\n not repeat the enumeration name.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n pub enum Cake {\n BlackForestCake,\n HummingbirdCake,\n BattenbergCake,\n }\n ```\n Could be written as:\n ```rust\n pub enum Cake {\n BlackForest,\n Hummingbird,\n Battenberg,\n }\n ```\n", + "applicability": null + }, + { + "id": "module_name_repetitions", + "id_span": { + "path": "clippy_lints/src/enum_variants.rs", + "line": 91 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Detects type names that are prefixed or suffixed by the containing module's name.\n\n **Why is this bad?** It requires the user to type the module name twice.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n mod cake {\n struct BlackForestCake;\n }\n ```\n Could be written as:\n ```rust\n mod cake {\n struct BlackForest;\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "module_inception", + "id_span": { + "path": "clippy_lints/src/enum_variants.rs", + "line": 121 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for modules that have the same name as their parent module\n\n **Why is this bad?** A typical beginner mistake is to have `mod foo;` and\n again `mod foo { ..\n }` in `foo.rs`.\n The expectation is that items inside the inner `mod foo { .. }` are then\n available\n through `foo::x`, but they are only available through\n `foo::foo::x`.\n If this is done on purpose, it would be better to choose a more\n representative module name.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n // lib.rs\n mod foo;\n // foo.rs\n mod foo {\n ...\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "eq_op", + "id_span": { + "path": "clippy_lints/src/eq_op.rs", + "line": 34 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for equal operands to comparison, logical and bitwise, difference and division binary operators (`==`, `>`, etc., `&&`,\n `||`, `&`, `|`, `^`, `-` and `/`).\n\n **Why is this bad?** This is usually just a typo or a copy and paste error.\n\n **Known problems:** False negatives: We had some false positives regarding\n calls (notably [racer](https://github.com/phildawes/racer) had one instance\n of `x.pop() && x.pop()`), so we removed matching any function or method\n calls. We may introduce a list of known pure functions in the future.\n\n **Example:**\n ```rust\n # let x = 1;\n if x + 1 == x + 1 {}\n ```\n or\n ```rust\n # let a = 3;\n # let b = 4;\n assert_eq!(a, a);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "op_ref", + "id_span": { + "path": "clippy_lints/src/eq_op.rs", + "line": 56 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for arguments to `==` which have their address taken to satisfy a bound\n and suggests to dereference the other argument instead\n\n **Why is this bad?** It is more idiomatic to dereference the other argument.\n\n **Known problems:** None\n\n **Example:**\n ```ignore\n // Bad\n &x == y\n\n // Good\n x == *y\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "erasing_op", + "id_span": { + "path": "clippy_lints/src/erasing_op.rs", + "line": 25 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for erasing operations, e.g., `x * 0`.\n **Why is this bad?** The whole expression can be replaced by zero.\n This is most likely not the intended outcome and should probably be\n corrected\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = 1;\n 0 / x;\n 0 * x;\n x & 0;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "boxed_local", + "id_span": { + "path": "clippy_lints/src/escape.rs", + "line": 43 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for usage of `Box` where an unboxed `T` would work fine.\n\n **Why is this bad?** This is an unnecessary allocation, and bad for\n performance. It is only necessary to allocate if you wish to move the box\n into something.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn foo(bar: usize) {}\n // Bad\n let x = Box::new(1);\n foo(*x);\n println!(\"{}\", *x);\n\n // Good\n let x = 1;\n foo(x);\n println!(\"{}\", x);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "redundant_closure", + "id_span": { + "path": "clippy_lints/src/eta_reduction.rs", + "line": 37 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for closures which just call another function where the function can be called directly. `unsafe` functions or calls where types\n get adjusted are ignored.\n\n **Why is this bad?** Needlessly creating a closure adds code for no benefit\n and gives the optimizer more work.\n\n **Known problems:** If creating the closure inside the closure has a side-\n effect then moving the closure creation out will change when that side-\n effect runs.\n See [#1439](https://github.com/rust-lang/rust-clippy/issues/1439) for more details.\n\n **Example:**\n ```rust,ignore\n // Bad\n xs.map(|x| foo(x))\n\n // Good\n xs.map(foo)\n ```\n where `foo(_)` is a plain function that takes the exact argument type of\n `x`.\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "redundant_closure_for_method_calls", + "id_span": { + "path": "clippy_lints/src/eta_reduction.rs", + "line": 61 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for closures which only invoke a method on the closure argument and can be replaced by referencing the method directly.\n\n **Why is this bad?** It's unnecessary to create the closure.\n\n **Known problems:** [#3071](https://github.com/rust-lang/rust-clippy/issues/3071),\n [#3942](https://github.com/rust-lang/rust-clippy/issues/3942),\n [#4002](https://github.com/rust-lang/rust-clippy/issues/4002)\n\n\n **Example:**\n ```rust,ignore\n Some('a').map(|s| s.to_uppercase());\n ```\n may be rewritten as\n ```rust,ignore\n Some('a').map(char::to_uppercase);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "eval_order_dependence", + "id_span": { + "path": "clippy_lints/src/eval_order_dependence.rs", + "line": 38 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for a read and a write to the same variable where whether the read occurs before or after the write depends on the evaluation\n order of sub-expressions.\n\n **Why is this bad?** It is often confusing to read. In addition, the\n sub-expression evaluation order for Rust is not well documented.\n\n **Known problems:** Code which intentionally depends on the evaluation\n order, or which is correct for any evaluation order.\n\n **Example:**\n ```rust\n let mut x = 0;\n\n // Bad\n let a = {\n x = 1;\n 1\n } + x;\n // Unclear whether a is 1 or 2.\n\n // Good\n let tmp = {\n x = 1;\n 1\n };\n let a = tmp + x;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "diverging_sub_expression", + "id_span": { + "path": "clippy_lints/src/eval_order_dependence.rs", + "line": 62 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for diverging calls that are not match arms or statements.\n\n **Why is this bad?** It is often confusing to read. In addition, the\n sub-expression evaluation order for Rust is not well documented.\n\n **Known problems:** Someone might want to use `some_bool || panic!()` as a\n shorthand.\n\n **Example:**\n ```rust,no_run\n # fn b() -> bool { true }\n # fn c() -> bool { true }\n let a = b() || panic!() || c();\n // `c()` is dead, `panic!()` is only called if `b()` returns `false`\n let x = (a, b, c, panic!());\n // can simply be replaced by `panic!()`\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "struct_excessive_bools", + "id_span": { + "path": "clippy_lints/src/excessive_bools.rs", + "line": 40 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for excessive use of bools in structs.\n\n **Why is this bad?** Excessive bools in a struct\n is often a sign that it's used as a state machine,\n which is much better implemented as an enum.\n If it's not the case, excessive bools usually benefit\n from refactoring into two-variant enums for better\n readability and API.\n\n **Known problems:** None.\n\n **Example:**\n Bad:\n ```rust\n struct S {\n is_pending: bool,\n is_processing: bool,\n is_finished: bool,\n }\n ```\n\n Good:\n ```rust\n enum S {\n Pending,\n Processing,\n Finished,\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "fn_params_excessive_bools", + "id_span": { + "path": "clippy_lints/src/excessive_bools.rs", + "line": 78 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for excessive use of bools in function definitions.\n\n **Why is this bad?** Calls to such functions\n are confusing and error prone, because it's\n hard to remember argument order and you have\n no type system support to back you up. Using\n two-variant enums instead of bools often makes\n API easier to use.\n\n **Known problems:** None.\n\n **Example:**\n Bad:\n ```rust,ignore\n fn f(is_round: bool, is_hot: bool) { ... }\n ```\n\n Good:\n ```rust,ignore\n enum Shape {\n Round,\n Spiky,\n }\n\n enum Temperature {\n Hot,\n IceCold,\n }\n\n fn f(shape: Shape, temperature: Temperature) { ... }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "exhaustive_enums", + "id_span": { + "path": "clippy_lints/src/exhaustive_items.rs", + "line": 34 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Warns on any exported `enum`s that are not tagged `#[non_exhaustive]`\n **Why is this bad?** Exhaustive enums are typically fine, but a project which does\n not wish to make a stability commitment around exported enums may wish to\n disable them by default.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n enum Foo {\n Bar,\n Baz\n }\n ```\n Use instead:\n ```rust\n #[non_exhaustive]\n enum Foo {\n Bar,\n Baz\n }\n ```\n", + "applicability": null + }, + { + "id": "exhaustive_structs", + "id_span": { + "path": "clippy_lints/src/exhaustive_items.rs", + "line": 64 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Warns on any exported `structs`s that are not tagged `#[non_exhaustive]`\n **Why is this bad?** Exhaustive structs are typically fine, but a project which does\n not wish to make a stability commitment around exported structs may wish to\n disable them by default.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n struct Foo {\n bar: u8,\n baz: String,\n }\n ```\n Use instead:\n ```rust\n #[non_exhaustive]\n struct Foo {\n bar: u8,\n baz: String,\n }\n ```\n", + "applicability": null + }, + { + "id": "exit", + "id_span": { + "path": "clippy_lints/src/exit.rs", + "line": 20 + }, + "group": "clippy::restriction", + "docs": " **What it does:** `exit()` terminates the program and doesn't provide a stack trace.\n\n **Why is this bad?** Ideally a program is terminated by finishing\n the main function.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n std::process::exit(0)\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "explicit_write", + "id_span": { + "path": "clippy_lints/src/explicit_write.rs", + "line": 25 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `write!()` / `writeln()!` which can be replaced with `(e)print!()` / `(e)println!()`\n\n **Why is this bad?** Using `(e)println! is clearer and more concise\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::io::Write;\n # let bar = \"furchtbar\";\n // this would be clearer as `eprintln!(\"foo: {:?}\", bar);`\n writeln!(&mut std::io::stderr(), \"foo: {:?}\", bar).unwrap();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "fallible_impl_from", + "id_span": { + "path": "clippy_lints/src/fallible_impl_from.rs", + "line": 45 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Checks for impls of `From<..>` that contain `panic!()` or `unwrap()`\n **Why is this bad?** `TryFrom` should be used if there's a possibility of failure.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n struct Foo(i32);\n\n // Bad\n impl From for Foo {\n fn from(s: String) -> Self {\n Foo(s.parse().unwrap())\n }\n }\n ```\n\n ```rust\n // Good\n struct Foo(i32);\n\n use std::convert::TryFrom;\n impl TryFrom for Foo {\n type Error = ();\n fn try_from(s: String) -> Result {\n if let Ok(parsed) = s.parse() {\n Ok(Foo(parsed))\n } else {\n Err(())\n }\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "float_equality_without_abs", + "id_span": { + "path": "clippy_lints/src/float_equality_without_abs.rs", + "line": 37 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for statements of the form `(a - b) < f32::EPSILON` or `(a - b) < f64::EPSILON`. Notes the missing `.abs()`.\n\n **Why is this bad?** The code without `.abs()` is more likely to have a bug.\n\n **Known problems:** If the user can ensure that b is larger than a, the `.abs()` is\n technically unneccessary. However, it will make the code more robust and doesn't have any\n large performance implications. If the abs call was deliberately left out for performance\n reasons, it is probably better to state this explicitly in the code, which then can be done\n with an allow.\n\n **Example:**\n\n ```rust\n pub fn is_roughly_equal(a: f32, b: f32) -> bool {\n (a - b) < f32::EPSILON\n }\n ```\n Use instead:\n ```rust\n pub fn is_roughly_equal(a: f32, b: f32) -> bool {\n (a - b).abs() < f32::EPSILON\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "excessive_precision", + "id_span": { + "path": "clippy_lints/src/float_literal.rs", + "line": 30 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for float literals with a precision greater than that supported by the underlying type.\n\n **Why is this bad?** Rust will truncate the literal silently.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let v: f32 = 0.123_456_789_9;\n println!(\"{}\", v); // 0.123_456_789\n\n // Good\n let v: f64 = 0.123_456_789_9;\n println!(\"{}\", v); // 0.123_456_789_9\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "lossy_float_literal", + "id_span": { + "path": "clippy_lints/src/float_literal.rs", + "line": 54 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for whole number float literals that cannot be represented as the underlying type without loss.\n\n **Why is this bad?** Rust will silently lose precision during\n conversion to a float.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let _: f32 = 16_777_217.0; // 16_777_216.0\n\n // Good\n let _: f32 = 16_777_216.0;\n let _: f64 = 16_777_217.0;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "imprecise_flops", + "id_span": { + "path": "clippy_lints/src/floating_point_arithmetic.rs", + "line": 45 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Looks for floating-point expressions that can be expressed using built-in methods to improve accuracy\n at the cost of performance.\n\n **Why is this bad?** Negatively impacts accuracy.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n let a = 3f32;\n let _ = a.powf(1.0 / 3.0);\n let _ = (1.0 + a).ln();\n let _ = a.exp() - 1.0;\n ```\n\n is better expressed as\n\n ```rust\n let a = 3f32;\n let _ = a.cbrt();\n let _ = a.ln_1p();\n let _ = a.exp_m1();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "suboptimal_flops", + "id_span": { + "path": "clippy_lints/src/floating_point_arithmetic.rs", + "line": 102 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Looks for floating-point expressions that can be expressed using built-in methods to improve both\n accuracy and performance.\n\n **Why is this bad?** Negatively impacts accuracy and performance.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n use std::f32::consts::E;\n\n let a = 3f32;\n let _ = (2f32).powf(a);\n let _ = E.powf(a);\n let _ = a.powf(1.0 / 2.0);\n let _ = a.log(2.0);\n let _ = a.log(10.0);\n let _ = a.log(E);\n let _ = a.powf(2.0);\n let _ = a * 2.0 + 4.0;\n let _ = if a < 0.0 {\n -a\n } else {\n a\n };\n let _ = if a < 0.0 {\n a\n } else {\n -a\n };\n ```\n\n is better expressed as\n\n ```rust\n use std::f32::consts::E;\n\n let a = 3f32;\n let _ = a.exp2();\n let _ = a.exp();\n let _ = a.sqrt();\n let _ = a.log2();\n let _ = a.log10();\n let _ = a.ln();\n let _ = a.powi(2);\n let _ = a.mul_add(2.0, 4.0);\n let _ = a.abs();\n let _ = -a.abs();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "useless_format", + "id_span": { + "path": "clippy_lints/src/format.rs", + "line": 37 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for the use of `format!(\"string literal with no argument\")` and `format!(\"{}\", foo)` where `foo` is a string.\n\n **Why is this bad?** There is no point of doing that. `format!(\"foo\")` can\n be replaced by `\"foo\".to_owned()` if you really need a `String`. The even\n worse `&format!(\"foo\")` is often encountered in the wild. `format!(\"{}\",\n foo)` can be replaced by `foo.clone()` if `foo: String` or `foo.to_owned()`\n if `foo: &str`.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n\n // Bad\n # let foo = \"foo\";\n format!(\"{}\", foo);\n\n // Good\n format!(\"foo\");\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "suspicious_assignment_formatting", + "id_span": { + "path": "clippy_lints/src/formatting.rs", + "line": 22 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for use of the non-existent `=*`, `=!` and `=-` operators.\n\n **Why is this bad?** This is either a typo of `*=`, `!=` or `-=` or\n confusing.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n a =- 42; // confusing, should it be `a -= 42` or `a = -42`?\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "suspicious_unary_op_formatting", + "id_span": { + "path": "clippy_lints/src/formatting.rs", + "line": 44 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks the formatting of a unary operator on the right hand side of a binary operator. It lints if there is no space between the binary and unary operators,\n but there is a space between the unary and its operand.\n\n **Why is this bad?** This is either a typo in the binary operator or confusing.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n if foo <- 30 { // this should be `foo < -30` but looks like a different operator\n }\n\n if foo &&! bar { // this should be `foo && !bar` but looks like a different operator\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "suspicious_else_formatting", + "id_span": { + "path": "clippy_lints/src/formatting.rs", + "line": 80 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for formatting of `else`. It lints if the `else` is followed immediately by a newline or the `else` seems to be missing.\n\n **Why is this bad?** This is probably some refactoring remnant, even if the\n code is correct, it might look confusing.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n if foo {\n } { // looks like an `else` is missing here\n }\n\n if foo {\n } if bar { // looks like an `else` is missing here\n }\n\n if foo {\n } else\n\n { // this is the `else` block of the previous `if`, but should it be?\n }\n\n if foo {\n } else\n\n if bar { // this is the `else` block of the previous `if`, but should it be?\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "possible_missing_comma", + "id_span": { + "path": "clippy_lints/src/formatting.rs", + "line": 100 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for possible missing comma in an array. It lints if an array element is a binary operator expression and it lies on two lines.\n\n **Why is this bad?** This could lead to unexpected results.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n let a = &[\n -1, -2, -3 // <= no comma here\n -4, -5, -6\n ];\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "from_over_into", + "id_span": { + "path": "clippy_lints/src/from_over_into.rs", + "line": 39 + }, + "group": "clippy::style", + "docs": " **What it does:** Searches for implementations of the `Into<..>` trait and suggests to implement `From<..>` instead.\n **Why is this bad?** According the std docs implementing `From<..>` is preferred since it gives you `Into<..>` for free where the reverse isn't true.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n struct StringWrapper(String);\n\n impl Into for String {\n fn into(self) -> StringWrapper {\n StringWrapper(self)\n }\n }\n ```\n Use instead:\n ```rust\n struct StringWrapper(String);\n\n impl From for StringWrapper {\n fn from(s: String) -> StringWrapper {\n StringWrapper(s)\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "from_str_radix_10", + "id_span": { + "path": "clippy_lints/src/from_str_radix_10.rs", + "line": 37 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for function invocations of the form `primitive::from_str_radix(s, 10)`\n\n **Why is this bad?**\n This specific common use case can be rewritten as `s.parse::()`\n (and in most cases, the turbofish can be removed), which reduces code length\n and complexity.\n\n **Known problems:**\n This lint may suggest using (&).parse() instead of .parse() directly\n in some cases, which is correct but adds unnecessary complexity to the code.\n\n **Example:**\n\n ```ignore\n let input: &str = get_input();\n let num = u16::from_str_radix(input, 10)?;\n ```\n Use instead:\n ```ignore\n let input: &str = get_input();\n let num: u16 = input.parse()?;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "too_many_arguments", + "id_span": { + "path": "clippy_lints/src/functions.rs", + "line": 39 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for functions with too many parameters.\n **Why is this bad?** Functions with lots of parameters are considered bad\n style and reduce readability (“what does the 5th parameter mean?”). Consider\n grouping some parameters into a new type.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct Color;\n fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) {\n // ..\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "too_many_lines", + "id_span": { + "path": "clippy_lints/src/functions.rs", + "line": 62 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for functions with a large amount of lines.\n **Why is this bad?** Functions with a lot of lines are harder to understand\n due to having to look at a larger amount of code to understand what the\n function is doing. Consider splitting the body of the function into\n multiple functions.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn im_too_long() {\n println!(\"\");\n // ... 100 more LoC\n println!(\"\");\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "not_unsafe_ptr_arg_deref", + "id_span": { + "path": "clippy_lints/src/functions.rs", + "line": 96 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for public functions that dereference raw pointer arguments but are not marked unsafe.\n\n **Why is this bad?** The function should probably be marked `unsafe`, since\n for an arbitrary raw pointer, there is no way of telling for sure if it is\n valid.\n\n **Known problems:**\n\n * It does not check functions recursively so if the pointer is passed to a\n private non-`unsafe` function which does the dereferencing, the lint won't\n trigger.\n * It only checks for arguments whose type are raw pointers, not raw pointers\n got from an argument in some other way (`fn foo(bar: &[*const u8])` or\n `some_argument.get_raw_ptr()`).\n\n **Example:**\n ```rust,ignore\n // Bad\n pub fn foo(x: *const u8) {\n println!(\"{}\", unsafe { *x });\n }\n\n // Good\n pub unsafe fn foo(x: *const u8) {\n println!(\"{}\", unsafe { *x });\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "must_use_unit", + "id_span": { + "path": "clippy_lints/src/functions.rs", + "line": 117 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for a [`#[must_use]`] attribute on unit-returning functions and methods.\n\n [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute\n\n **Why is this bad?** Unit values are useless. The attribute is likely\n a remnant of a refactoring that removed the return type.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n #[must_use]\n fn useless() { }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "double_must_use", + "id_span": { + "path": "clippy_lints/src/functions.rs", + "line": 142 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for a [`#[must_use]`] attribute without further information on functions and methods that return a type already\n marked as `#[must_use]`.\n\n [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute\n\n **Why is this bad?** The attribute isn't needed. Not using the result\n will already be reported. Alternatively, one can add some text to the\n attribute to improve the lint message.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n #[must_use]\n fn double_must_use() -> Result<(), ()> {\n unimplemented!();\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "must_use_candidate", + "id_span": { + "path": "clippy_lints/src/functions.rs", + "line": 170 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for public functions that have no [`#[must_use]`] attribute, but return something not already marked\n must-use, have no mutable arg and mutate no statics.\n\n [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute\n\n **Why is this bad?** Not bad at all, this lint just shows places where\n you could add the attribute.\n\n **Known problems:** The lint only checks the arguments for mutable\n types without looking if they are actually changed. On the other hand,\n it also ignores a broad range of potentially interesting side effects,\n because we cannot decide whether the programmer intends the function to\n be called for the side effect or the result. Expect many false\n positives. At least we don't lint if the result type is unit or already\n `#[must_use]`.\n\n **Examples:**\n ```rust\n // this could be annotated with `#[must_use]`.\n fn id(t: T) -> T { t }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "result_unit_err", + "id_span": { + "path": "clippy_lints/src/functions.rs", + "line": 216 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for public functions that return a `Result` with an `Err` type of `()`. It suggests using a custom type that\n implements [`std::error::Error`].\n\n **Why is this bad?** Unit does not implement `Error` and carries no\n further information about what went wrong.\n\n **Known problems:** Of course, this lint assumes that `Result` is used\n for a fallible operation (which is after all the intended use). However\n code may opt to (mis)use it as a basic two-variant-enum. In that case,\n the suggestion is misguided, and the code should use a custom enum\n instead.\n\n **Examples:**\n ```rust\n pub fn read_u8() -> Result { Err(()) }\n ```\n should become\n ```rust,should_panic\n use std::fmt;\n\n #[derive(Debug)]\n pub struct EndOfStream;\n\n impl fmt::Display for EndOfStream {\n fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n write!(f, \"End of Stream\")\n }\n }\n\n impl std::error::Error for EndOfStream { }\n\n pub fn read_u8() -> Result { Err(EndOfStream) }\n# fn main() {\n# read_u8().unwrap();\n# }\n ```\n\n Note that there are crates that simplify creating the error type, e.g.\n [`thiserror`](https://docs.rs/thiserror).\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "future_not_send", + "id_span": { + "path": "clippy_lints/src/future_not_send.rs", + "line": 44 + }, + "group": "clippy::nursery", + "docs": " **What it does:** This lint requires Future implementations returned from functions and methods to implement the `Send` marker trait. It is mostly\n used by library authors (public and internal) that target an audience where\n multithreaded executors are likely to be used for running these Futures.\n\n **Why is this bad?** A Future implementation captures some state that it\n needs to eventually produce its final value. When targeting a multithreaded\n executor (which is the norm on non-embedded devices) this means that this\n state may need to be transported to other threads, in other words the\n whole Future needs to implement the `Send` marker trait. If it does not,\n then the resulting Future cannot be submitted to a thread pool in the\n end user’s code.\n\n Especially for generic functions it can be confusing to leave the\n discovery of this problem to the end user: the reported error location\n will be far from its cause and can in many cases not even be fixed without\n modifying the library where the offending Future implementation is\n produced.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n async fn not_send(bytes: std::rc::Rc<[u8]>) {}\n ```\n Use instead:\n ```rust\n async fn is_send(bytes: std::sync::Arc<[u8]>) {}\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "get_last_with_len", + "id_span": { + "path": "clippy_lints/src/get_last_with_len.rs", + "line": 40 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for using `x.get(x.len() - 1)` instead of `x.last()`.\n\n **Why is this bad?** Using `x.last()` is easier to read and has the same\n result.\n\n Note that using `x[x.len() - 1]` is semantically different from\n `x.last()`. Indexing into the array will panic on out-of-bounds\n accesses, while `x.get()` and `x.last()` will return `None`.\n\n There is another lint (get_unwrap) that covers the case of using\n `x.get(index).unwrap()` instead of `x[index]`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let x = vec![2, 3, 5];\n let last_element = x.get(x.len() - 1);\n\n // Good\n let x = vec![2, 3, 5];\n let last_element = x.last();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "identity_op", + "id_span": { + "path": "clippy_lints/src/identity_op.rs", + "line": 24 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for identity operations, e.g., `x + 0`.\n **Why is this bad?** This code can be removed without changing the\n meaning. So it just obscures what's going on. Delete it mercilessly.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = 1;\n x / 1 + 0 * 1 - 0 | 0;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "if_let_mutex", + "id_span": { + "path": "clippy_lints/src/if_let_mutex.rs", + "line": 36 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for `Mutex::lock` calls in `if let` expression with lock calls in any of the else blocks.\n\n **Why is this bad?** The Mutex lock remains held for the whole\n `if let ... else` block and deadlocks.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,ignore\n if let Ok(thing) = mutex.lock() {\n do_thing();\n } else {\n mutex.lock();\n }\n ```\n Should be written\n ```rust,ignore\n let locked = mutex.lock();\n if let Ok(thing) = locked {\n do_thing(thing);\n } else {\n use_locked(locked);\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "if_let_some_result", + "id_span": { + "path": "clippy_lints/src/if_let_some_result.rs", + "line": 34 + }, + "group": "clippy::style", + "docs": " **What it does:*** Checks for unnecessary `ok()` in if let.\n **Why is this bad?** Calling `ok()` in if let is unnecessary, instead match\n on `Ok(pat)`\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n for i in iter {\n if let Some(value) = i.parse().ok() {\n vec.push(value)\n }\n }\n ```\n Could be written:\n\n ```ignore\n for i in iter {\n if let Ok(value) = i.parse() {\n vec.push(value)\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "if_not_else", + "id_span": { + "path": "clippy_lints/src/if_not_else.rs", + "line": 43 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for usage of `!` or `!=` in an if condition with an else branch.\n\n **Why is this bad?** Negations reduce the readability of statements.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let v: Vec = vec![];\n # fn a() {}\n # fn b() {}\n if !v.is_empty() {\n a()\n } else {\n b()\n }\n ```\n\n Could be written:\n\n ```rust\n # let v: Vec = vec![];\n # fn a() {}\n # fn b() {}\n if v.is_empty() {\n b()\n } else {\n a()\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "implicit_return", + "id_span": { + "path": "clippy_lints/src/implicit_return.rs", + "line": 33 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for missing return statements at the end of a block.\n **Why is this bad?** Actually omitting the return keyword is idiomatic Rust code. Programmers\n coming from other languages might prefer the expressiveness of `return`. It's possible to miss\n the last returning statement because the only difference is a missing `;`. Especially in bigger\n code with multiple return paths having a `return` keyword makes it easier to find the\n corresponding statements.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn foo(x: usize) -> usize {\n x\n }\n ```\n add return\n ```rust\n fn foo(x: usize) -> usize {\n return x;\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "implicit_saturating_sub", + "id_span": { + "path": "clippy_lints/src/implicit_saturating_sub.rs", + "line": 32 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for implicit saturating subtraction.\n **Why is this bad?** Simplicity and readability. Instead we can easily use an builtin function.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let end: u32 = 10;\n let start: u32 = 5;\n\n let mut i: u32 = end - start;\n\n // Bad\n if i != 0 {\n i -= 1;\n }\n\n // Good\n i = i.saturating_sub(1);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "inconsistent_struct_constructor", + "id_span": { + "path": "clippy_lints/src/inconsistent_struct_constructor.rs", + "line": 58 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for struct constructors where the order of the field init shorthand in the constructor is inconsistent with the order in the struct definition.\n\n **Why is this bad?** Since the order of fields in a constructor doesn't affect the\n resulted instance as the below example indicates,\n\n ```rust\n #[derive(Debug, PartialEq, Eq)]\n struct Foo {\n x: i32,\n y: i32,\n }\n let x = 1;\n let y = 2;\n\n // This assertion never fails.\n assert_eq!(Foo { x, y }, Foo { y, x });\n ```\n\n inconsistent order means nothing and just decreases readability and consistency.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n struct Foo {\n x: i32,\n y: i32,\n }\n let x = 1;\n let y = 2;\n Foo { y, x };\n ```\n\n Use instead:\n ```rust\n # struct Foo {\n # x: i32,\n # y: i32,\n # }\n # let x = 1;\n # let y = 2;\n Foo { x, y };\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "out_of_bounds_indexing", + "id_span": { + "path": "clippy_lints/src/indexing_slicing.rs", + "line": 32 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for out of bounds array indexing with a constant index.\n\n **Why is this bad?** This will always panic at runtime.\n\n **Known problems:** Hopefully none.\n\n **Example:**\n ```no_run\n # #![allow(const_err)]\n let x = [1, 2, 3, 4];\n\n // Bad\n x[9];\n &x[2..9];\n\n // Good\n x[0];\n x[3];\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "indexing_slicing", + "id_span": { + "path": "clippy_lints/src/indexing_slicing.rs", + "line": 81 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for usage of indexing or slicing. Arrays are special cases, this lint does report on arrays if we can tell that slicing operations are in bounds and does not\n lint on constant `usize` indexing on arrays because that is handled by rustc's `const_err` lint.\n\n **Why is this bad?** Indexing and slicing can panic at runtime and there are\n safe alternatives.\n\n **Known problems:** Hopefully none.\n\n **Example:**\n ```rust,no_run\n // Vector\n let x = vec![0; 5];\n\n // Bad\n x[2];\n &x[2..100];\n &x[2..];\n &x[..100];\n\n // Good\n x.get(2);\n x.get(2..100);\n x.get(2..);\n x.get(..100);\n\n // Array\n let y = [0, 1, 2, 3];\n\n // Bad\n &y[10..100];\n &y[10..];\n &y[..100];\n\n // Good\n &y[2..];\n &y[..2];\n &y[0..3];\n y.get(10);\n y.get(10..100);\n y.get(10..);\n y.get(..100);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "infinite_iter", + "id_span": { + "path": "clippy_lints/src/infinite_iter.rs", + "line": 21 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for iteration that is guaranteed to be infinite.\n **Why is this bad?** While there may be places where this is acceptable\n (e.g., in event streams), in most cases this is simply an error.\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n use std::iter;\n\n iter::repeat(1_u8).collect::>();\n ```\n", + "applicability": null + }, + { + "id": "maybe_infinite_iter", + "id_span": { + "path": "clippy_lints/src/infinite_iter.rs", + "line": 40 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for iteration that may be infinite.\n **Why is this bad?** While there may be places where this is acceptable\n (e.g., in event streams), in most cases this is simply an error.\n\n **Known problems:** The code may have a condition to stop iteration, but\n this lint is not clever enough to analyze it.\n\n **Example:**\n ```rust\n let infinite_iter = 0..;\n [0..].iter().zip(infinite_iter.take_while(|x| *x > 5));\n ```\n", + "applicability": null + }, + { + "id": "multiple_inherent_impl", + "id_span": { + "path": "clippy_lints/src/inherent_impl.rs", + "line": 37 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for multiple inherent implementations of a struct\n **Why is this bad?** Splitting the implementation of a type makes the code harder to navigate.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n struct X;\n impl X {\n fn one() {}\n }\n impl X {\n fn other() {}\n }\n ```\n\n Could be written:\n\n ```rust\n struct X;\n impl X {\n fn one() {}\n fn other() {}\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "inherent_to_string", + "id_span": { + "path": "clippy_lints/src/inherent_to_string.rs", + "line": 44 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for the definition of inherent methods with a signature of `to_string(&self) -> String`.\n **Why is this bad?** This method is also implicitly defined if a type implements the `Display` trait. As the functionality of `Display` is much more versatile, it should be preferred.\n\n **Known problems:** None\n\n ** Example:**\n\n ```rust\n // Bad\n pub struct A;\n\n impl A {\n pub fn to_string(&self) -> String {\n \"I am A\".to_string()\n }\n }\n ```\n\n ```rust\n // Good\n use std::fmt;\n\n pub struct A;\n\n impl fmt::Display for A {\n fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n write!(f, \"I am A\")\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "inherent_to_string_shadow_display", + "id_span": { + "path": "clippy_lints/src/inherent_to_string.rs", + "line": 89 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for the definition of inherent methods with a signature of `to_string(&self) -> String` and if the type implementing this method also implements the `Display` trait.\n **Why is this bad?** This method is also implicitly defined if a type implements the `Display` trait. The less versatile inherent method will then shadow the implementation introduced by `Display`.\n\n **Known problems:** None\n\n ** Example:**\n\n ```rust\n // Bad\n use std::fmt;\n\n pub struct A;\n\n impl A {\n pub fn to_string(&self) -> String {\n \"I am A\".to_string()\n }\n }\n\n impl fmt::Display for A {\n fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n write!(f, \"I am A, too\")\n }\n }\n ```\n\n ```rust\n // Good\n use std::fmt;\n\n pub struct A;\n\n impl fmt::Display for A {\n fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n write!(f, \"I am A\")\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "inline_fn_without_body", + "id_span": { + "path": "clippy_lints/src/inline_fn_without_body.rs", + "line": 27 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for `#[inline]` on trait methods without bodies\n **Why is this bad?** Only implementations of trait methods may be inlined.\n The inline attribute is ignored for trait methods without bodies.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n trait Animal {\n #[inline]\n fn name(&self) -> &'static str;\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "int_plus_one", + "id_span": { + "path": "clippy_lints/src/int_plus_one.rs", + "line": 31 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `x >= y + 1` or `x - 1 >= y` (and `<=`) in a block\n **Why is this bad?** Readability -- better to use `> y` instead of `>= y + 1`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = 1;\n # let y = 1;\n if x >= y + 1 {}\n ```\n\n Could be written as:\n\n ```rust\n # let x = 1;\n # let y = 1;\n if x > y {}\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "integer_division", + "id_span": { + "path": "clippy_lints/src/integer_division.rs", + "line": 26 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for division of integers\n **Why is this bad?** When outside of some very specific algorithms,\n integer division is very often a mistake because it discards the\n remainder.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n let x = 3 / 2;\n println!(\"{}\", x);\n\n // Good\n let x = 3f32 / 2f32;\n println!(\"{}\", x);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "items_after_statements", + "id_span": { + "path": "clippy_lints/src/items_after_statements.rs", + "line": 48 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for items declared after some statement in a block.\n **Why is this bad?** Items live for the entire scope they are declared\n in. But statements are processed in order. This might cause confusion as\n it's hard to figure out which item is meant in a statement.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n fn foo() {\n println!(\"cake\");\n }\n\n fn main() {\n foo(); // prints \"foo\"\n fn foo() {\n println!(\"foo\");\n }\n foo(); // prints \"foo\"\n }\n ```\n\n ```rust\n // Good\n fn foo() {\n println!(\"cake\");\n }\n\n fn main() {\n fn foo() {\n println!(\"foo\");\n }\n foo(); // prints \"foo\"\n foo(); // prints \"foo\"\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "large_const_arrays", + "id_span": { + "path": "clippy_lints/src/large_const_arrays.rs", + "line": 30 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for large `const` arrays that should be defined as `static` instead.\n\n **Why is this bad?** Performance: const variables are inlined upon use.\n Static items result in only one instance and has a fixed location in memory.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n // Bad\n pub const a = [0u32; 1_000_000];\n\n // Good\n pub static a = [0u32; 1_000_000];\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "large_enum_variant", + "id_span": { + "path": "clippy_lints/src/large_enum_variant.rs", + "line": 39 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for large size differences between variants on `enum`s.\n\n **Why is this bad?** Enum size is bounded by the largest variant. Having a\n large variant can penalize the memory layout of that enum.\n\n **Known problems:** This lint obviously cannot take the distribution of\n variants in your running program into account. It is possible that the\n smaller variants make up less than 1% of all instances, in which case\n the overhead is negligible and the boxing is counter-productive. Always\n measure the change this lint suggests.\n\n **Example:**\n\n ```rust\n // Bad\n enum Test {\n A(i32),\n B([i32; 8000]),\n }\n\n // Possibly better\n enum Test2 {\n A(i32),\n B(Box<[i32; 8000]>),\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "large_stack_arrays", + "id_span": { + "path": "clippy_lints/src/large_stack_arrays.rs", + "line": 23 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for local arrays that may be too large.\n **Why is this bad?** Large local arrays may cause stack overflow.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n let a = [0u32; 1_000_000];\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "len_zero", + "id_span": { + "path": "clippy_lints/src/len_zero.rs", + "line": 41 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for getting the length of something via `.len()` just to compare to zero, and suggests using `.is_empty()` where applicable.\n\n **Why is this bad?** Some structures can answer `.is_empty()` much faster\n than calculating their length. So it is good to get into the habit of using\n `.is_empty()`, and having it is cheap.\n Besides, it makes the intent clearer than a manual comparison in some contexts.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n if x.len() == 0 {\n ..\n }\n if y.len() != 0 {\n ..\n }\n ```\n instead use\n ```ignore\n if x.is_empty() {\n ..\n }\n if !y.is_empty() {\n ..\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "len_without_is_empty", + "id_span": { + "path": "clippy_lints/src/len_zero.rs", + "line": 66 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for items that implement `.len()` but not `.is_empty()`.\n\n **Why is this bad?** It is good custom to have both methods, because for\n some data structures, asking about the length will be a costly operation,\n whereas `.is_empty()` can usually answer in constant time. Also it used to\n lead to false positives on the [`len_zero`](#len_zero) lint – currently that\n lint will ignore such entities.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n impl X {\n pub fn len(&self) -> usize {\n ..\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "comparison_to_empty", + "id_span": { + "path": "clippy_lints/src/len_zero.rs", + "line": 103 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for comparing to an empty slice such as `\"\"` or `[]`, and suggests using `.is_empty()` where applicable.\n\n **Why is this bad?** Some structures can answer `.is_empty()` much faster\n than checking for equality. So it is good to get into the habit of using\n `.is_empty()`, and having it is cheap.\n Besides, it makes the intent clearer than a manual comparison in some contexts.\n\n **Known problems:** None.\n\n **Example:**\n\n ```ignore\n if s == \"\" {\n ..\n }\n\n if arr == [] {\n ..\n }\n ```\n Use instead:\n ```ignore\n if s.is_empty() {\n ..\n }\n\n if arr.is_empty() {\n ..\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "useless_let_if_seq", + "id_span": { + "path": "clippy_lints/src/let_if_seq.rs", + "line": 49 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Checks for variable declarations immediately followed by a conditional affectation.\n\n **Why is this bad?** This is not idiomatic Rust.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n let foo;\n\n if bar() {\n foo = 42;\n } else {\n foo = 0;\n }\n\n let mut baz = None;\n\n if bar() {\n baz = Some(42);\n }\n ```\n\n should be written\n\n ```rust,ignore\n let foo = if bar() {\n 42\n } else {\n 0\n };\n\n let baz = if bar() {\n Some(42)\n } else {\n None\n };\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "HasPlaceholders" + } + }, + { + "id": "let_underscore_must_use", + "id_span": { + "path": "clippy_lints/src/let_underscore.rs", + "line": 29 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for `let _ = ` where expr is #[must_use]\n\n **Why is this bad?** It's better to explicitly\n handle the value of a #[must_use] expr\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn f() -> Result {\n Ok(0)\n }\n\n let _ = f();\n // is_ok() is marked #[must_use]\n let _ = f().is_ok();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "let_underscore_lock", + "id_span": { + "path": "clippy_lints/src/let_underscore.rs", + "line": 56 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for `let _ = sync_lock`\n **Why is this bad?** This statement immediately drops the lock instead of\n extending its lifetime to the end of the scope, which is often not intended.\n To extend lock lifetime to the end of the scope, use an underscore-prefixed\n name instead (i.e. _lock). If you want to explicitly drop the lock,\n `std::mem::drop` conveys your intention better and is less error-prone.\n\n **Known problems:** None.\n\n **Example:**\n\n Bad:\n ```rust,ignore\n let _ = mutex.lock();\n ```\n\n Good:\n ```rust,ignore\n let _lock = mutex.lock();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "let_underscore_drop", + "id_span": { + "path": "clippy_lints/src/let_underscore.rs", + "line": 97 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for `let _ = ` where expr has a type that implements `Drop`\n\n **Why is this bad?** This statement immediately drops the initializer\n expression instead of extending its lifetime to the end of the scope, which\n is often not intended. To extend the expression's lifetime to the end of the\n scope, use an underscore-prefixed name instead (i.e. _var). If you want to\n explicitly drop the expression, `std::mem::drop` conveys your intention\n better and is less error-prone.\n\n **Known problems:** None.\n\n **Example:**\n\n Bad:\n ```rust,ignore\n struct Droppable;\n impl Drop for Droppable {\n fn drop(&mut self) {}\n }\n {\n let _ = Droppable;\n // ^ dropped here\n /* more code */\n }\n ```\n\n Good:\n ```rust,ignore\n {\n let _droppable = Droppable;\n /* more code */\n // dropped at end of scope\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "needless_lifetimes", + "id_span": { + "path": "clippy_lints/src/lifetimes.rs", + "line": 46 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for lifetime annotations which can be removed by relying on lifetime elision.\n\n **Why is this bad?** The additional lifetimes make the code look more\n complicated, while there is nothing out of the ordinary going on. Removing\n them leads to more readable code.\n\n **Known problems:**\n - We bail out if the function has a `where` clause where lifetimes\n are mentioned due to potenial false positives.\n - Lifetime bounds such as `impl Foo + 'a` and `T: 'a` must be elided with the\n placeholder notation `'_` because the fully elided notation leaves the type bound to `'static`.\n\n **Example:**\n ```rust\n // Bad: unnecessary lifetime annotations\n fn in_and_out<'a>(x: &'a u8, y: u8) -> &'a u8 {\n x\n }\n\n // Good\n fn elided(x: &u8, y: u8) -> &u8 {\n x\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "extra_unused_lifetimes", + "id_span": { + "path": "clippy_lints/src/lifetimes.rs", + "line": 74 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for lifetimes in generics that are never used anywhere else.\n\n **Why is this bad?** The additional lifetimes make the code look more\n complicated, while there is nothing out of the ordinary going on. Removing\n them leads to more readable code.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad: unnecessary lifetimes\n fn unused_lifetime<'a>(x: u8) {\n // ..\n }\n\n // Good\n fn no_lifetime(x: u8) {\n // ...\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "unreadable_literal", + "id_span": { + "path": "clippy_lints/src/literal_representation.rs", + "line": 33 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Warns if a long integral or floating-point constant does not contain underscores.\n\n **Why is this bad?** Reading long numbers is difficult without separators.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let x: u64 = 61864918973511;\n\n // Good\n let x: u64 = 61_864_918_973_511;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "mistyped_literal_suffixes", + "id_span": { + "path": "clippy_lints/src/literal_representation.rs", + "line": 57 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Warns for mistyped suffix in literals\n **Why is this bad?** This is most probably a typo\n\n **Known problems:**\n - Recommends a signed suffix, even though the number might be too big and an unsigned\n suffix is required\n - Does not match on `_127` since that is a valid grouping for decimal and octal numbers\n\n **Example:**\n\n ```rust\n // Probably mistyped\n 2_32;\n\n // Good\n 2_i32;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "inconsistent_digit_grouping", + "id_span": { + "path": "clippy_lints/src/literal_representation.rs", + "line": 80 + }, + "group": "clippy::style", + "docs": " **What it does:** Warns if an integral or floating-point constant is grouped inconsistently with underscores.\n\n **Why is this bad?** Readers may incorrectly interpret inconsistently\n grouped digits.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let x: u64 = 618_64_9189_73_511;\n\n // Good\n let x: u64 = 61_864_918_973_511;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "unusual_byte_groupings", + "id_span": { + "path": "clippy_lints/src/literal_representation.rs", + "line": 99 + }, + "group": "clippy::style", + "docs": " **What it does:** Warns if hexadecimal or binary literals are not grouped by nibble or byte.\n\n **Why is this bad?** Negatively impacts readability.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let x: u32 = 0xFFF_FFF;\n let y: u8 = 0b01_011_101;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "large_digit_groups", + "id_span": { + "path": "clippy_lints/src/literal_representation.rs", + "line": 118 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Warns if the digits of an integral or floating-point constant are grouped into groups that\n are too large.\n\n **Why is this bad?** Negatively impacts readability.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let x: u64 = 6186491_8973511;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "decimal_literal_representation", + "id_span": { + "path": "clippy_lints/src/literal_representation.rs", + "line": 136 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Warns if there is a better representation for a numeric literal.\n **Why is this bad?** Especially for big powers of 2 a hexadecimal representation is more\n readable than a decimal representation.\n\n **Known problems:** None.\n\n **Example:**\n\n `255` => `0xFF`\n `65_535` => `0xFFFF`\n `4_042_322_160` => `0xF0F0_F0F0`\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "manual_memcpy", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 57 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for for-loops that manually copy items between slices that could be optimized by having a memcpy.\n\n **Why is this bad?** It is not as fast as a memcpy.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let src = vec![1];\n # let mut dst = vec![0; 65];\n for i in 0..src.len() {\n dst[i + 64] = src[i];\n }\n ```\n Could be written as:\n ```rust\n # let src = vec![1];\n # let mut dst = vec![0; 65];\n dst[64..(src.len() + 64)].clone_from_slice(&src[..]);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "needless_range_loop", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 85 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for looping over the range of `0..len` of some collection just to get the values by index.\n\n **Why is this bad?** Just iterating the collection itself makes the intent\n more clear and is probably faster.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let vec = vec!['a', 'b', 'c'];\n for i in 0..vec.len() {\n println!(\"{}\", vec[i]);\n }\n ```\n Could be written as:\n ```rust\n let vec = vec!['a', 'b', 'c'];\n for i in vec {\n println!(\"{}\", i);\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "explicit_iter_loop", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 114 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for loops on `x.iter()` where `&x` will do, and suggests the latter.\n\n **Why is this bad?** Readability.\n\n **Known problems:** False negatives. We currently only warn on some known\n types.\n\n **Example:**\n ```rust\n // with `y` a `Vec` or slice:\n # let y = vec![1];\n for x in y.iter() {\n // ..\n }\n ```\n can be rewritten to\n ```rust\n # let y = vec![1];\n for x in &y {\n // ..\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "explicit_into_iter_loop", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 142 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for loops on `y.into_iter()` where `y` will do, and suggests the latter.\n\n **Why is this bad?** Readability.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n # let y = vec![1];\n // with `y` a `Vec` or slice:\n for x in y.into_iter() {\n // ..\n }\n ```\n can be rewritten to\n ```rust\n # let y = vec![1];\n for x in y {\n // ..\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "iter_next_loop", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 165 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for loops on `x.next()`.\n **Why is this bad?** `next()` returns either `Some(value)` if there was a\n value, or `None` otherwise. The insidious thing is that `Option<_>`\n implements `IntoIterator`, so that possibly one value will be iterated,\n leading to some hard to find bugs. No one will want to write such code\n [except to win an Underhanded Rust\n Contest](https://www.reddit.com/r/rust/comments/3hb0wm/underhanded_rust_contest/cu5yuhr).\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n for x in y.next() {\n ..\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "for_loops_over_fallibles", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 208 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for `for` loops over `Option` or `Result` values.\n **Why is this bad?** Readability. This is more clearly expressed as an `if\n let`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let opt = Some(1);\n\n // Bad\n for x in opt {\n // ..\n }\n\n // Good\n if let Some(x) = opt {\n // ..\n }\n ```\n\n // or\n\n ```rust\n # let res: Result = Ok(1);\n\n // Bad\n for x in &res {\n // ..\n }\n\n // Good\n if let Ok(x) = res {\n // ..\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "while_let_loop", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 237 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Detects `loop + match` combinations that are easier written as a `while let` loop.\n\n **Why is this bad?** The `while let` loop is usually shorter and more\n readable.\n\n **Known problems:** Sometimes the wrong binding is displayed ([#383](https://github.com/rust-lang/rust-clippy/issues/383)).\n\n **Example:**\n ```rust,no_run\n # let y = Some(1);\n loop {\n let x = match y {\n Some(x) => x,\n None => break,\n };\n // .. do something with x\n }\n // is easier written as\n while let Some(x) = y {\n // .. do something with x\n };\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "HasPlaceholders" + } + }, + { + "id": "needless_collect", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 259 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for functions collecting an iterator when collect is not needed.\n\n **Why is this bad?** `collect` causes the allocation of a new data structure,\n when this allocation may not be needed.\n\n **Known problems:**\n None\n\n **Example:**\n ```rust\n # let iterator = vec![1].into_iter();\n let len = iterator.clone().collect::>().len();\n // should be\n let len = iterator.count();\n ```\n", + "applicability": { + "is_multi_suggestion": true, + "applicability": "MachineApplicable" + } + }, + { + "id": "explicit_counter_loop", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 289 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks `for` loops over slices with an explicit counter and suggests the use of `.enumerate()`.\n\n **Why is it bad?** Using `.enumerate()` makes the intent more clear,\n declutters the code and may be faster in some instances.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let v = vec![1];\n # fn bar(bar: usize, baz: usize) {}\n let mut i = 0;\n for item in &v {\n bar(i, *item);\n i += 1;\n }\n ```\n Could be written as\n ```rust\n # let v = vec![1];\n # fn bar(bar: usize, baz: usize) {}\n for (i, item) in v.iter().enumerate() { bar(i, *item); }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "empty_loop", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 322 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for empty `loop` expressions.\n **Why is this bad?** These busy loops burn CPU cycles without doing\n anything. It is _almost always_ a better idea to `panic!` than to have\n a busy loop.\n\n If panicking isn't possible, think of the environment and either:\n - block on something\n - sleep the thread for some microseconds\n - yield or pause the thread\n\n For `std` targets, this can be done with\n [`std::thread::sleep`](https://doc.rust-lang.org/std/thread/fn.sleep.html)\n or [`std::thread::yield_now`](https://doc.rust-lang.org/std/thread/fn.yield_now.html).\n\n For `no_std` targets, doing this is more complicated, especially because\n `#[panic_handler]`s can't panic. To stop/pause the thread, you will\n probably need to invoke some target-specific intrinsic. Examples include:\n - [`x86_64::instructions::hlt`](https://docs.rs/x86_64/0.12.2/x86_64/instructions/fn.hlt.html)\n - [`cortex_m::asm::wfi`](https://docs.rs/cortex-m/0.6.3/cortex_m/asm/fn.wfi.html)\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n loop {}\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "while_let_on_iterator", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 341 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `while let` expressions on iterators.\n **Why is this bad?** Readability. A simple `for` loop is shorter and conveys\n the intent better.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n while let Some(val) = iter() {\n ..\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "for_kv_map", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 369 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for iterating a map (`HashMap` or `BTreeMap`) and ignoring either the keys or values.\n\n **Why is this bad?** Readability. There are `keys` and `values` methods that\n can be used to express that don't need the values or keys.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n for (k, _) in &map {\n ..\n }\n ```\n\n could be replaced by\n\n ```ignore\n for k in map.keys() {\n ..\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "never_loop", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 390 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for loops that will always `break`, `return` or `continue` an outer loop.\n\n **Why is this bad?** This loop never loops, all it does is obfuscating the\n code.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n loop {\n ..;\n break;\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "mut_range_bound", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 410 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for loops which have a range bound that is a mutable variable\n **Why is this bad?** One might think that modifying the mutable variable changes the loop bounds\n\n **Known problems:** None\n\n **Example:**\n ```rust\n let mut foo = 42;\n for i in 0..foo {\n foo -= 1;\n println!(\"{}\", i); // prints numbers from 0 to 42, not 0 to 21\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "while_immutable_condition", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 433 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks whether variables used within while loop condition can be (and are) mutated in the body.\n\n **Why is this bad?** If the condition is unchanged, entering the body of the loop\n will lead to an infinite loop.\n\n **Known problems:** If the `while`-loop is in a closure, the check for mutation of the\n condition variables in the body can cause false negatives. For example when only `Upvar` `a` is\n in the condition and only `Upvar` `b` gets mutated in the body, the lint will not trigger.\n\n **Example:**\n ```rust\n let i = 0;\n while i > 10 {\n println!(\"let me loop forever!\");\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "same_item_push", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 466 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks whether a for loop is being used to push a constant value into a Vec.\n\n **Why is this bad?** This kind of operation can be expressed more succinctly with\n `vec![item;SIZE]` or `vec.resize(NEW_SIZE, item)` and using these alternatives may also\n have better performance.\n **Known problems:** None\n\n **Example:**\n ```rust\n let item1 = 2;\n let item2 = 3;\n let mut vec: Vec = Vec::new();\n for _ in 0..20 {\n vec.push(item1);\n }\n for _ in 0..30 {\n vec.push(item2);\n }\n ```\n could be written as\n ```rust\n let item1 = 2;\n let item2 = 3;\n let mut vec: Vec = vec![item1; 20];\n vec.resize(20 + 30, item2);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "single_element_loop", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 491 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks whether a for loop has a single element.\n **Why is this bad?** There is no reason to have a loop of a\n single element.\n **Known problems:** None\n\n **Example:**\n ```rust\n let item1 = 2;\n for item in &[item1] {\n println!(\"{}\", item);\n }\n ```\n could be written as\n ```rust\n let item1 = 2;\n let item = &item1;\n println!(\"{}\", item);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "manual_flatten", + "id_span": { + "path": "clippy_lints/src/loops.rs", + "line": 522 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Check for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the iterator element is used.\n\n **Why is this bad?** It is verbose and can be simplified\n by first calling the `flatten` method on the `Iterator`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let x = vec![Some(1), Some(2), Some(3)];\n for n in x {\n if let Some(n) = n {\n println!(\"{}\", n);\n }\n }\n ```\n Use instead:\n ```rust\n let x = vec![Some(1), Some(2), Some(3)];\n for n in x.into_iter().flatten() {\n println!(\"{}\", n);\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "macro_use_imports", + "id_span": { + "path": "clippy_lints/src/macro_use.rs", + "line": 25 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for `#[macro_use] use...`.\n **Why is this bad?** Since the Rust 2018 edition you can import\n macro's directly, this is considered idiomatic.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n #[macro_use]\n use some_macro;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "main_recursion", + "id_span": { + "path": "clippy_lints/src/main_recursion.rs", + "line": 22 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for recursion using the entrypoint.\n **Why is this bad?** Apart from special setups (which we could detect following attributes like #![no_std]),\n recursing into main() seems like an unintuitive antipattern we should be able to detect.\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n fn main() {\n main();\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "manual_async_fn", + "id_span": { + "path": "clippy_lints/src/manual_async_fn.rs", + "line": 32 + }, + "group": "clippy::style", + "docs": " **What it does:** It checks for manual implementations of `async` functions.\n **Why is this bad?** It's more idiomatic to use the dedicated syntax.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n use std::future::Future;\n\n fn foo() -> impl Future { async { 42 } }\n ```\n Use instead:\n ```rust\n async fn foo() -> i32 { 42 }\n ```\n", + "applicability": { + "is_multi_suggestion": true, + "applicability": "MachineApplicable" + } + }, + { + "id": "manual_map", + "id_span": { + "path": "clippy_lints/src/manual_map.rs", + "line": 36 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Checks for usages of `match` which could be implemented using `map`\n **Why is this bad?** Using the `map` method is clearer and more concise.\n\n **Known problems:** `map` is not capable of representing some control flow which works fine in `match`.\n\n **Example:**\n\n ```rust\n match Some(0) {\n Some(x) => Some(x + 1),\n None => None,\n };\n ```\n Use instead:\n ```rust\n Some(0).map(|x| x + 1);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "manual_non_exhaustive", + "id_span": { + "path": "clippy_lints/src/manual_non_exhaustive.rs", + "line": 56 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for manual implementations of the non-exhaustive pattern.\n **Why is this bad?** Using the #[non_exhaustive] attribute expresses better the intent\n and allows possible optimizations when applied to enums.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n struct S {\n pub a: i32,\n pub b: i32,\n _c: (),\n }\n\n enum E {\n A,\n B,\n #[doc(hidden)]\n _C,\n }\n\n struct T(pub i32, pub i32, ());\n ```\n Use instead:\n ```rust\n #[non_exhaustive]\n struct S {\n pub a: i32,\n pub b: i32,\n }\n\n #[non_exhaustive]\n enum E {\n A,\n B,\n }\n\n #[non_exhaustive]\n struct T(pub i32, pub i32);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "manual_ok_or", + "id_span": { + "path": "clippy_lints/src/manual_ok_or.rs", + "line": 34 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Finds patterns that reimplement `Option::ok_or`.\n\n **Why is this bad?**\n Concise code helps focusing on behavior instead of boilerplate.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n let foo: Option = None;\n foo.map_or(Err(\"error\"), |v| Ok(v));\n ```\n\n Use instead:\n ```rust\n let foo: Option = None;\n foo.ok_or(\"error\");\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "manual_strip", + "id_span": { + "path": "clippy_lints/src/manual_strip.rs", + "line": 52 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing using\n the pattern's length.\n\n **Why is this bad?**\n Using `str:strip_{prefix,suffix}` is safer and may have better performance as there is no\n slicing which may panic and the compiler does not need to insert this panic code. It is\n also sometimes more readable as it removes the need for duplicating or storing the pattern\n used by `str::{starts,ends}_with` and in the slicing.\n\n **Known problems:**\n None.\n\n **Example:**\n\n ```rust\n let s = \"hello, world!\";\n if s.starts_with(\"hello, \") {\n assert_eq!(s[\"hello, \".len()..].to_uppercase(), \"WORLD!\");\n }\n ```\n Use instead:\n ```rust\n let s = \"hello, world!\";\n if let Some(end) = s.strip_prefix(\"hello, \") {\n assert_eq!(end.to_uppercase(), \"WORLD!\");\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "manual_unwrap_or", + "id_span": { + "path": "clippy_lints/src/manual_unwrap_or.rs", + "line": 36 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`.\n\n **Why is this bad?**\n Concise code helps focusing on behavior instead of boilerplate.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let foo: Option = None;\n match foo {\n Some(v) => v,\n None => 1,\n };\n ```\n\n Use instead:\n ```rust\n let foo: Option = None;\n foo.unwrap_or(1);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "map_clone", + "id_span": { + "path": "clippy_lints/src/map_clone.rs", + "line": 40 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for usage of `map(|x| x.clone())` or dereferencing closures for `Copy` types, on `Iterator` or `Option`,\n and suggests `cloned()` or `copied()` instead\n\n **Why is this bad?** Readability, this can be written more concisely\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n let x = vec![42, 43];\n let y = x.iter();\n let z = y.map(|i| *i);\n ```\n\n The correct use would be:\n\n ```rust\n let x = vec![42, 43];\n let y = x.iter();\n let z = y.cloned();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "map_err_ignore", + "id_span": { + "path": "clippy_lints/src/map_err_ignore.rs", + "line": 101 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for instances of `map_err(|_| Some::Enum)`\n **Why is this bad?** This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error\n\n **Known problems:** None.\n\n **Example:**\n Before:\n ```rust\n use std::fmt;\n\n #[derive(Debug)]\n enum Error {\n Indivisible,\n Remainder(u8),\n }\n\n impl fmt::Display for Error {\n fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n match self {\n Error::Indivisible => write!(f, \"could not divide input by three\"),\n Error::Remainder(remainder) => write!(\n f,\n \"input is not divisible by three, remainder = {}\",\n remainder\n ),\n }\n }\n }\n\n impl std::error::Error for Error {}\n\n fn divisible_by_3(input: &str) -> Result<(), Error> {\n input\n .parse::()\n .map_err(|_| Error::Indivisible)\n .map(|v| v % 3)\n .and_then(|remainder| {\n if remainder == 0 {\n Ok(())\n } else {\n Err(Error::Remainder(remainder as u8))\n }\n })\n }\n ```\n\n After:\n ```rust\n use std::{fmt, num::ParseIntError};\n\n #[derive(Debug)]\n enum Error {\n Indivisible(ParseIntError),\n Remainder(u8),\n }\n\n impl fmt::Display for Error {\n fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n match self {\n Error::Indivisible(_) => write!(f, \"could not divide input by three\"),\n Error::Remainder(remainder) => write!(\n f,\n \"input is not divisible by three, remainder = {}\",\n remainder\n ),\n }\n }\n }\n\n impl std::error::Error for Error {\n fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {\n match self {\n Error::Indivisible(source) => Some(source),\n _ => None,\n }\n }\n }\n\n fn divisible_by_3(input: &str) -> Result<(), Error> {\n input\n .parse::()\n .map_err(Error::Indivisible)\n .map(|v| v % 3)\n .and_then(|remainder| {\n if remainder == 0 {\n Ok(())\n } else {\n Err(Error::Remainder(remainder as u8))\n }\n })\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "map_identity", + "id_span": { + "path": "clippy_lints/src/map_identity.rs", + "line": 30 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for instances of `map(f)` where `f` is the identity function.\n **Why is this bad?** It can be written more concisely without the call to `map`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let x = [1, 2, 3];\n let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();\n ```\n Use instead:\n ```rust\n let x = [1, 2, 3];\n let y: Vec<_> = x.iter().map(|x| 2*x).collect();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "option_map_unit_fn", + "id_span": { + "path": "clippy_lints/src/map_unit_fn.rs", + "line": 48 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `option.map(f)` where f is a function or closure that returns the unit type `()`.\n\n **Why is this bad?** Readability, this can be written more clearly with\n an if let statement\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # fn do_stuff() -> Option { Some(String::new()) }\n # fn log_err_msg(foo: String) -> Option { Some(foo) }\n # fn format_msg(foo: String) -> String { String::new() }\n let x: Option = do_stuff();\n x.map(log_err_msg);\n # let x: Option = do_stuff();\n x.map(|msg| log_err_msg(format_msg(msg)));\n ```\n\n The correct use would be:\n\n ```rust\n # fn do_stuff() -> Option { Some(String::new()) }\n # fn log_err_msg(foo: String) -> Option { Some(foo) }\n # fn format_msg(foo: String) -> String { String::new() }\n let x: Option = do_stuff();\n if let Some(msg) = x {\n log_err_msg(msg);\n }\n\n # let x: Option = do_stuff();\n if let Some(msg) = x {\n log_err_msg(format_msg(msg));\n }\n ```\n", + "applicability": null + }, + { + "id": "result_map_unit_fn", + "id_span": { + "path": "clippy_lints/src/map_unit_fn.rs", + "line": 89 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `result.map(f)` where f is a function or closure that returns the unit type `()`.\n\n **Why is this bad?** Readability, this can be written more clearly with\n an if let statement\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # fn do_stuff() -> Result { Ok(String::new()) }\n # fn log_err_msg(foo: String) -> Result { Ok(foo) }\n # fn format_msg(foo: String) -> String { String::new() }\n let x: Result = do_stuff();\n x.map(log_err_msg);\n # let x: Result = do_stuff();\n x.map(|msg| log_err_msg(format_msg(msg)));\n ```\n\n The correct use would be:\n\n ```rust\n # fn do_stuff() -> Result { Ok(String::new()) }\n # fn log_err_msg(foo: String) -> Result { Ok(foo) }\n # fn format_msg(foo: String) -> String { String::new() }\n let x: Result = do_stuff();\n if let Ok(msg) = x {\n log_err_msg(msg);\n };\n # let x: Result = do_stuff();\n if let Ok(msg) = x {\n log_err_msg(format_msg(msg));\n };\n ```\n", + "applicability": null + }, + { + "id": "match_on_vec_items", + "id_span": { + "path": "clippy_lints/src/match_on_vec_items.rs", + "line": 41 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for `match vec[idx]` or `match vec[n..m]`.\n **Why is this bad?** This can panic at runtime.\n\n **Known problems:** None.\n\n **Example:**\n ```rust, no_run\n let arr = vec![0, 1, 2, 3];\n let idx = 1;\n\n // Bad\n match arr[idx] {\n 0 => println!(\"{}\", 0),\n 1 => println!(\"{}\", 3),\n _ => {},\n }\n ```\n Use instead:\n ```rust, no_run\n let arr = vec![0, 1, 2, 3];\n let idx = 1;\n\n // Good\n match arr.get(idx) {\n Some(0) => println!(\"{}\", 0),\n Some(1) => println!(\"{}\", 3),\n _ => {},\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "single_match", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 55 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for matches with a single arm where an `if let` will usually suffice.\n\n **Why is this bad?** Just readability – `if let` nests less than a `match`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn bar(stool: &str) {}\n # let x = Some(\"abc\");\n // Bad\n match x {\n Some(ref foo) => bar(foo),\n _ => (),\n }\n\n // Good\n if let Some(ref foo) = x {\n bar(foo);\n }\n ```\n", + "applicability": null + }, + { + "id": "single_match_else", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 94 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for matches with two arms where an `if let else` will usually suffice.\n\n **Why is this bad?** Just readability – `if let` nests less than a `match`.\n\n **Known problems:** Personal style preferences may differ.\n\n **Example:**\n\n Using `match`:\n\n ```rust\n # fn bar(foo: &usize) {}\n # let other_ref: usize = 1;\n # let x: Option<&usize> = Some(&1);\n match x {\n Some(ref foo) => bar(foo),\n _ => bar(&other_ref),\n }\n ```\n\n Using `if let` with `else`:\n\n ```rust\n # fn bar(foo: &usize) {}\n # let other_ref: usize = 1;\n # let x: Option<&usize> = Some(&1);\n if let Some(ref foo) = x {\n bar(foo);\n } else {\n bar(&other_ref);\n }\n ```\n", + "applicability": null + }, + { + "id": "match_ref_pats", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 125 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for matches where all arms match a reference, suggesting to remove the reference and deref the matched expression\n instead. It also checks for `if let &foo = bar` blocks.\n\n **Why is this bad?** It just makes the code less readable. That reference\n destructuring adds nothing to the code.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n // Bad\n match x {\n &A(ref y) => foo(y),\n &B => bar(),\n _ => frob(&x),\n }\n\n // Good\n match *x {\n A(ref y) => foo(y),\n B => bar(),\n _ => frob(x),\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "match_bool", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 159 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for matches where match expression is a `bool`. It suggests to replace the expression with an `if...else` block.\n\n **Why is this bad?** It makes the code less readable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn foo() {}\n # fn bar() {}\n let condition: bool = true;\n match condition {\n true => foo(),\n false => bar(),\n }\n ```\n Use if/else instead:\n ```rust\n # fn foo() {}\n # fn bar() {}\n let condition: bool = true;\n if condition {\n foo();\n } else {\n bar();\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "HasPlaceholders" + } + }, + { + "id": "match_overlapping_arm", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 181 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for overlapping match arms.\n **Why is this bad?** It is likely to be an error and if not, makes the code\n less obvious.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = 5;\n match x {\n 1...10 => println!(\"1 ... 10\"),\n 5...15 => println!(\"5 ... 15\"),\n _ => (),\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "match_wild_err_arm", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 203 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for arm which matches all errors with `Err(_)` and take drastic actions like `panic!`.\n\n **Why is this bad?** It is generally a bad practice, similar to\n catching all exceptions in java with `catch(Exception)`\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x: Result = Ok(3);\n match x {\n Ok(_) => println!(\"ok\"),\n Err(_) => panic!(\"err\"),\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "match_as_ref", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 229 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for match which is used to add a reference to an `Option` value.\n\n **Why is this bad?** Using `as_ref()` or `as_mut()` instead is shorter.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x: Option<()> = None;\n\n // Bad\n let r: Option<&()> = match x {\n None => None,\n Some(ref v) => Some(v),\n };\n\n // Good\n let r: Option<&()> = x.as_ref();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "wildcard_enum_match_arm", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 258 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for wildcard enum matches using `_`.\n **Why is this bad?** New enum variants added by library updates can be missed.\n\n **Known problems:** Suggested replacements may be incorrect if guards exhaustively cover some\n variants, and also may not use correct path to enum if it's not present in the current scope.\n\n **Example:**\n ```rust\n # enum Foo { A(usize), B(usize) }\n # let x = Foo::B(1);\n // Bad\n match x {\n Foo::A(_) => {},\n _ => {},\n }\n\n // Good\n match x {\n Foo::A(_) => {},\n Foo::B(_) => {},\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "match_wildcard_for_single_variants", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 290 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for wildcard enum matches for a single variant.\n **Why is this bad?** New enum variants added by library updates can be missed.\n\n **Known problems:** Suggested replacements may not use correct path to enum\n if it's not present in the current scope.\n\n **Example:**\n\n ```rust\n # enum Foo { A, B, C }\n # let x = Foo::B;\n // Bad\n match x {\n Foo::A => {},\n Foo::B => {},\n _ => {},\n }\n\n // Good\n match x {\n Foo::A => {},\n Foo::B => {},\n Foo::C => {},\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "wildcard_in_or_patterns", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 317 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for wildcard pattern used with others patterns in same match arm.\n **Why is this bad?** Wildcard pattern already covers any other pattern as it will match anyway.\n It makes the code less readable, especially to spot wildcard pattern use in match arm.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n match \"foo\" {\n \"a\" => {},\n \"bar\" | _ => {},\n }\n\n // Good\n match \"foo\" {\n \"a\" => {},\n _ => {},\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "infallible_destructuring_match", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 352 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for matches being used to destructure a single-variant enum or tuple struct where a `let` will suffice.\n\n **Why is this bad?** Just readability – `let` doesn't nest, whereas a `match` does.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n enum Wrapper {\n Data(i32),\n }\n\n let wrapper = Wrapper::Data(42);\n\n let data = match wrapper {\n Wrapper::Data(i) => i,\n };\n ```\n\n The correct use would be:\n ```rust\n enum Wrapper {\n Data(i32),\n }\n\n let wrapper = Wrapper::Data(42);\n let Wrapper::Data(data) = wrapper;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "match_single_binding", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 380 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for useless match that binds to only one value.\n **Why is this bad?** Readability and needless complexity.\n\n **Known problems:** Suggested replacements may be incorrect when `match`\n is actually binding temporary value, bringing a 'dropped while borrowed' error.\n\n **Example:**\n ```rust\n # let a = 1;\n # let b = 2;\n\n // Bad\n match (a, b) {\n (c, d) => {\n // useless match\n }\n }\n\n // Good\n let (c, d) = (a, b);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "rest_pat_in_fully_bound_structs", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 410 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched.\n **Why is this bad?** Correctness and readability. It's like having a wildcard pattern after\n matching all enum variants explicitly.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct A { a: i32 }\n let a = A { a: 5 };\n\n // Bad\n match a {\n A { a: 5, .. } => {},\n _ => {},\n }\n\n // Good\n match a {\n A { a: 5 } => {},\n _ => {},\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "redundant_pattern_matching", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 458 + }, + "group": "clippy::style", + "docs": " **What it does:** Lint for redundant pattern matching over `Result`, `Option`, `std::task::Poll` or `std::net::IpAddr`\n\n **Why is this bad?** It's more concise and clear to just use the proper\n utility function\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # use std::task::Poll;\n # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};\n if let Ok(_) = Ok::(42) {}\n if let Err(_) = Err::(42) {}\n if let None = None::<()> {}\n if let Some(_) = Some(42) {}\n if let Poll::Pending = Poll::Pending::<()> {}\n if let Poll::Ready(_) = Poll::Ready(42) {}\n if let IpAddr::V4(_) = IpAddr::V4(Ipv4Addr::LOCALHOST) {}\n if let IpAddr::V6(_) = IpAddr::V6(Ipv6Addr::LOCALHOST) {}\n match Ok::(42) {\n Ok(_) => true,\n Err(_) => false,\n };\n ```\n\n The more idiomatic use would be:\n\n ```rust\n # use std::task::Poll;\n # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};\n if Ok::(42).is_ok() {}\n if Err::(42).is_err() {}\n if None::<()>.is_none() {}\n if Some(42).is_some() {}\n if Poll::Pending::<()>.is_pending() {}\n if Poll::Ready(42).is_ready() {}\n if IpAddr::V4(Ipv4Addr::LOCALHOST).is_ipv4() {}\n if IpAddr::V6(Ipv6Addr::LOCALHOST).is_ipv6() {}\n Ok::(42).is_ok();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "match_like_matches_macro", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 491 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `match` or `if let` expressions producing a `bool` that could be written using `matches!`\n\n **Why is this bad?** Readability and needless complexity.\n\n **Known problems:** This lint falsely triggers, if there are arms with\n `cfg` attributes that remove an arm evaluating to `false`.\n\n **Example:**\n ```rust\n let x = Some(5);\n\n // Bad\n let a = match x {\n Some(0) => true,\n _ => false,\n };\n\n let a = if let Some(0) = x {\n true\n } else {\n false\n };\n\n // Good\n let a = matches!(x, Some(0));\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "match_same_arms", + "id_span": { + "path": "clippy_lints/src/matches.rs", + "line": 532 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for `match` with identical arm bodies.\n **Why is this bad?** This is probably a copy & paste error. If arm bodies\n are the same on purpose, you can factor them\n [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).\n\n **Known problems:** False positive possible with order dependent `match`\n (see issue\n [#860](https://github.com/rust-lang/rust-clippy/issues/860)).\n\n **Example:**\n ```rust,ignore\n match foo {\n Bar => bar(),\n Quz => quz(),\n Baz => bar(), // <= oops\n }\n ```\n\n This should probably be\n ```rust,ignore\n match foo {\n Bar => bar(),\n Quz => quz(),\n Baz => baz(), // <= fixed\n }\n ```\n\n or if the original code was not a typo:\n ```rust,ignore\n match foo {\n Bar | Baz => bar(), // <= shows the intent better\n Quz => quz(),\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "mem_discriminant_non_enum", + "id_span": { + "path": "clippy_lints/src/mem_discriminant.rs", + "line": 25 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for calls of `mem::discriminant()` on a non-enum type.\n **Why is this bad?** The value of `mem::discriminant()` on non-enum types\n is unspecified.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n use std::mem;\n\n mem::discriminant(&\"hello\");\n mem::discriminant(&&Some(2));\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "mem_forget", + "id_span": { + "path": "clippy_lints/src/mem_forget.rs", + "line": 21 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for usage of `std::mem::forget(t)` where `t` is `Drop`.\n\n **Why is this bad?** `std::mem::forget(t)` prevents `t` from running its\n destructor, possibly causing leaks.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::mem;\n # use std::rc::Rc;\n mem::forget(Rc::new(55))\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "mem_replace_option_with_none", + "id_span": { + "path": "clippy_lints/src/mem_replace.rs", + "line": 37 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `mem::replace()` on an `Option` with `None`.\n\n **Why is this bad?** `Option` already has the method `take()` for\n taking its current value (Some(..) or None) and replacing it with\n `None`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n use std::mem;\n\n let mut an_option = Some(0);\n let replaced = mem::replace(&mut an_option, None);\n ```\n Is better expressed with:\n ```rust\n let mut an_option = Some(0);\n let taken = an_option.take();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "mem_replace_with_uninit", + "id_span": { + "path": "clippy_lints/src/mem_replace.rs", + "line": 69 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for `mem::replace(&mut _, mem::uninitialized())` and `mem::replace(&mut _, mem::zeroed())`.\n\n **Why is this bad?** This will lead to undefined behavior even if the\n value is overwritten later, because the uninitialized value may be\n observed in the case of a panic.\n\n **Known problems:** None.\n\n **Example:**\n\n ```\n use std::mem;\n# fn may_panic(v: Vec) -> Vec { v }\n\n #[allow(deprecated, invalid_value)]\n fn myfunc (v: &mut Vec) {\n let taken_v = unsafe { mem::replace(v, mem::uninitialized()) };\n let new_v = may_panic(taken_v); // undefined behavior on panic\n mem::forget(mem::replace(v, new_v));\n }\n ```\n\n The [take_mut](https://docs.rs/take_mut) crate offers a sound solution,\n at the cost of either lazily creating a replacement value or aborting\n on panic, to ensure that the uninitialized value cannot be observed.\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "mem_replace_with_default", + "id_span": { + "path": "clippy_lints/src/mem_replace.rs", + "line": 93 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `std::mem::replace` on a value of type `T` with `T::default()`.\n\n **Why is this bad?** `std::mem` module already has the method `take` to\n take the current value and replace it with the default value of that type.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let mut text = String::from(\"foo\");\n let replaced = std::mem::replace(&mut text, String::default());\n ```\n Is better expressed with:\n ```rust\n let mut text = String::from(\"foo\");\n let taken = std::mem::take(&mut text);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "unwrap_used", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 82 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s.\n **Why is this bad?** It is better to handle the `None` or `Err` case,\n or at least call `.expect(_)` with a more helpful message. Still, for a lot of\n quick-and-dirty code, `unwrap` is a good choice, which is why this lint is\n `Allow` by default.\n\n `result.unwrap()` will let the thread panic on `Err` values.\n Normally, you want to implement more sophisticated error handling,\n and propagate errors upwards with `?` operator.\n\n Even if you want to panic on errors, not all `Error`s implement good\n messages on display. Therefore, it may be beneficial to look at the places\n where they may get displayed. Activate this lint to do just that.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n # let opt = Some(1);\n\n // Bad\n opt.unwrap();\n\n // Good\n opt.expect(\"more helpful message\");\n ```\n\n // or\n\n ```rust\n # let res: Result = Ok(1);\n\n // Bad\n res.unwrap();\n\n // Good\n res.expect(\"more helpful message\");\n ```\n", + "applicability": null + }, + { + "id": "expect_used", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 124 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for `.expect()` calls on `Option`s and `Result`s.\n **Why is this bad?** Usually it is better to handle the `None` or `Err` case.\n Still, for a lot of quick-and-dirty code, `expect` is a good choice, which is why\n this lint is `Allow` by default.\n\n `result.expect()` will let the thread panic on `Err`\n values. Normally, you want to implement more sophisticated error handling,\n and propagate errors upwards with `?` operator.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust,ignore\n # let opt = Some(1);\n\n // Bad\n opt.expect(\"one\");\n\n // Good\n let opt = Some(1);\n opt?;\n ```\n\n // or\n\n ```rust\n # let res: Result = Ok(1);\n\n // Bad\n res.expect(\"one\");\n\n // Good\n res?;\n # Ok::<(), ()>(())\n ```\n", + "applicability": null + }, + { + "id": "should_implement_trait", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 153 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for methods that should live in a trait implementation of a `std` trait (see [llogiq's blog\n post](http://llogiq.github.io/2015/07/30/traits.html) for further\n information) instead of an inherent implementation.\n\n **Why is this bad?** Implementing the traits improve ergonomics for users of\n the code, often with very little cost. Also people seeing a `mul(...)`\n method\n may expect `*` to work equally, so you should have good reason to disappoint\n them.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n struct X;\n impl X {\n fn add(&self, other: &X) -> X {\n // ..\n # X\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "wrong_self_convention", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 186 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for methods with certain name prefixes and which doesn't match how self is taken. The actual rules are:\n\n |Prefix |`self` taken |\n |-------|----------------------|\n |`as_` |`&self` or `&mut self`|\n |`from_`| none |\n |`into_`|`self` |\n |`is_` |`&self` or none |\n |`to_` |`&self` |\n\n **Why is this bad?** Consistency breeds readability. If you follow the\n conventions, your users won't be surprised that they, e.g., need to supply a\n mutable reference to a `as_..` function.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct X;\n impl X {\n fn as_str(self) -> &'static str {\n // ..\n # \"\"\n }\n }\n ```\n", + "applicability": null + }, + { + "id": "wrong_pub_self_convention", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 210 + }, + "group": "clippy::restriction", + "docs": " **What it does:** This is the same as [`wrong_self_convention`](#wrong_self_convention), but for public items.\n\n **Why is this bad?** See [`wrong_self_convention`](#wrong_self_convention).\n\n **Known problems:** Actually *renaming* the function may break clients if\n the function is part of the public interface. In that case, be mindful of\n the stability guarantees you've given your users.\n\n **Example:**\n ```rust\n # struct X;\n impl<'a> X {\n pub fn as_str(self) -> &'a str {\n \"foo\"\n }\n }\n ```\n", + "applicability": null + }, + { + "id": "ok_expect", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 233 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for usage of `ok().expect(..)`.\n **Why is this bad?** Because you usually call `expect()` on the `Result`\n directly to get a better error message.\n\n **Known problems:** The error type needs to implement `Debug`\n\n **Example:**\n ```rust\n # let x = Ok::<_, ()>(());\n\n // Bad\n x.ok().expect(\"why did I do this again?\");\n\n // Good\n x.expect(\"why did I do this again?\");\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "map_unwrap_or", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 270 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for usage of `option.map(_).unwrap_or(_)` or `option.map(_).unwrap_or_else(_)` or `result.map(_).unwrap_or_else(_)`.\n\n **Why is this bad?** Readability, these can be written more concisely (resp.) as\n `option.map_or(_, _)`, `option.map_or_else(_, _)` and `result.map_or_else(_, _)`.\n\n **Known problems:** The order of the arguments is not in execution order\n\n **Examples:**\n ```rust\n # let x = Some(1);\n\n // Bad\n x.map(|a| a + 1).unwrap_or(0);\n\n // Good\n x.map_or(0, |a| a + 1);\n ```\n\n // or\n\n ```rust\n # let x: Result = Ok(1);\n # fn some_function(foo: ()) -> usize { 1 }\n\n // Bad\n x.map(|a| a + 1).unwrap_or_else(some_function);\n\n // Good\n x.map_or_else(some_function, |a| a + 1);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "option_map_or_none", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 293 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for usage of `_.map_or(None, _)`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.and_then(_)`.\n\n **Known problems:** The order of the arguments is not in execution order.\n\n **Example:**\n ```rust\n # let opt = Some(1);\n\n // Bad\n opt.map_or(None, |a| Some(a + 1));\n\n // Good\n opt.and_then(|a| Some(a + 1));\n ```\n", + "applicability": null + }, + { + "id": "result_map_or_into_option", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 319 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for usage of `_.map_or(None, Some)`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.ok()`.\n\n **Known problems:** None.\n\n **Example:**\n\n Bad:\n ```rust\n # let r: Result = Ok(1);\n assert_eq!(Some(1), r.map_or(None, Some));\n ```\n\n Good:\n ```rust\n # let r: Result = Ok(1);\n assert_eq!(Some(1), r.ok());\n ```\n", + "applicability": null + }, + { + "id": "bind_instead_of_map", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 352 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or `_.or_else(|x| Err(y))`.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.map(|x| y)` or `_.map_err(|x| y)`.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n # fn opt() -> Option<&'static str> { Some(\"42\") }\n # fn res() -> Result<&'static str, &'static str> { Ok(\"42\") }\n let _ = opt().and_then(|s| Some(s.len()));\n let _ = res().and_then(|s| if s.len() == 42 { Ok(10) } else { Ok(20) });\n let _ = res().or_else(|s| if s.len() == 42 { Err(10) } else { Err(20) });\n ```\n\n The correct use would be:\n\n ```rust\n # fn opt() -> Option<&'static str> { Some(\"42\") }\n # fn res() -> Result<&'static str, &'static str> { Ok(\"42\") }\n let _ = opt().map(|s| s.len());\n let _ = res().map(|s| if s.len() == 42 { 10 } else { 20 });\n let _ = res().map_err(|s| if s.len() == 42 { 10 } else { 20 });\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "filter_next", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 375 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `_.filter(_).next()`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.find(_)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let vec = vec![1];\n vec.iter().filter(|x| **x == 0).next();\n ```\n Could be written as\n ```rust\n # let vec = vec![1];\n vec.iter().find(|x| **x == 0);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "skip_while_next", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 398 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `_.skip_while(condition).next()`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.find(!condition)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let vec = vec![1];\n vec.iter().skip_while(|x| **x == 0).next();\n ```\n Could be written as\n ```rust\n # let vec = vec![1];\n vec.iter().find(|x| **x != 0);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "map_flatten", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 421 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for usage of `_.map(_).flatten(_)`,\n **Why is this bad?** Readability, this can be written more concisely as\n `_.flat_map(_)`\n\n **Known problems:**\n\n **Example:**\n ```rust\n let vec = vec![vec![1]];\n\n // Bad\n vec.iter().map(|x| x.iter()).flatten();\n\n // Good\n vec.iter().flat_map(|x| x.iter());\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "filter_map", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 450 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for usage of `_.filter(_).map(_)`, `_.filter(_).flat_map(_)`, `_.filter_map(_).flat_map(_)` and similar.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.filter_map(_)`.\n\n **Known problems:** Often requires a condition + Option/Iterator creation\n inside the closure.\n\n **Example:**\n ```rust\n let vec = vec![1];\n\n // Bad\n vec.iter().filter(|x| **x == 0).map(|x| *x * 2);\n\n // Good\n vec.iter().filter_map(|x| if *x == 0 {\n Some(*x * 2)\n } else {\n None\n });\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "manual_filter_map", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 476 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `_.filter(_).map(_)` that can be written more simply as `filter_map(_)`.\n\n **Why is this bad?** Redundant code in the `filter` and `map` operations is poor style and\n less performant.\n\n **Known problems:** None.\n\n **Example:**\n Bad:\n ```rust\n (0_i32..10)\n .filter(|n| n.checked_add(1).is_some())\n .map(|n| n.checked_add(1).unwrap());\n ```\n\n Good:\n ```rust\n (0_i32..10).filter_map(|n| n.checked_add(1));\n ```\n", + "applicability": null + }, + { + "id": "manual_find_map", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 502 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `_.find(_).map(_)` that can be written more simply as `find_map(_)`.\n\n **Why is this bad?** Redundant code in the `find` and `map` operations is poor style and\n less performant.\n\n **Known problems:** None.\n\n **Example:**\n Bad:\n ```rust\n (0_i32..10)\n .find(|n| n.checked_add(1).is_some())\n .map(|n| n.checked_add(1).unwrap());\n ```\n\n Good:\n ```rust\n (0_i32..10).find_map(|n| n.checked_add(1));\n ```\n", + "applicability": null + }, + { + "id": "filter_map_next", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 524 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for usage of `_.filter_map(_).next()`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.find_map(_)`.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n (0..3).filter_map(|x| if x == 2 { Some(x) } else { None }).next();\n ```\n Can be written as\n\n ```rust\n (0..3).find_map(|x| if x == 2 { Some(x) } else { None });\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "flat_map_identity", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 546 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `flat_map(|x| x)`.\n **Why is this bad?** Readability, this can be written more concisely by using `flatten`.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n # let iter = vec![vec![0]].into_iter();\n iter.flat_map(|x| x);\n ```\n Can be written as\n ```rust\n # let iter = vec![vec![0]].into_iter();\n iter.flatten();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "search_is_some", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 570 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for an iterator or string search (such as `find()`, `position()`, or `rposition()`) followed by a call to `is_some()`.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.any(_)` or `_.contains(_)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let vec = vec![1];\n vec.iter().find(|x| **x == 0).is_some();\n ```\n Could be written as\n ```rust\n # let vec = vec![1];\n vec.iter().any(|x| *x == 0);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "chars_next_cmp", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 594 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for usage of `.chars().next()` on a `str` to check if it starts with a given char.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.starts_with(_)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let name = \"foo\";\n if name.chars().next() == Some('_') {};\n ```\n Could be written as\n ```rust\n let name = \"foo\";\n if name.starts_with('_') {};\n ```\n", + "applicability": null + }, + { + "id": "or_fun_call", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 625 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`, etc., and suggests to use `or_else`, `unwrap_or_else`, etc., or\n `unwrap_or_default` instead.\n\n **Why is this bad?** The function will always be called and potentially\n allocate an object acting as the default.\n\n **Known problems:** If the function has side-effects, not calling it will\n change the semantic of the program, but you shouldn't rely on that anyway.\n\n **Example:**\n ```rust\n # let foo = Some(String::new());\n foo.unwrap_or(String::new());\n ```\n this can instead be written:\n ```rust\n # let foo = Some(String::new());\n foo.unwrap_or_else(String::new);\n ```\n or\n ```rust\n # let foo = Some(String::new());\n foo.unwrap_or_default();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "HasPlaceholders" + } + }, + { + "id": "expect_fun_call", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 660 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`, etc., and suggests to use `unwrap_or_else` instead\n\n **Why is this bad?** The function will always be called.\n\n **Known problems:** If the function has side-effects, not calling it will\n change the semantics of the program, but you shouldn't rely on that anyway.\n\n **Example:**\n ```rust\n # let foo = Some(String::new());\n # let err_code = \"418\";\n # let err_msg = \"I'm a teapot\";\n foo.expect(&format!(\"Err {}: {}\", err_code, err_msg));\n ```\n or\n ```rust\n # let foo = Some(String::new());\n # let err_code = \"418\";\n # let err_msg = \"I'm a teapot\";\n foo.expect(format!(\"Err {}: {}\", err_code, err_msg).as_str());\n ```\n this can instead be written:\n ```rust\n # let foo = Some(String::new());\n # let err_code = \"418\";\n # let err_msg = \"I'm a teapot\";\n foo.unwrap_or_else(|| panic!(\"Err {}: {}\", err_code, err_msg));\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "clone_on_copy", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 677 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `.clone()` on a `Copy` type.\n **Why is this bad?** The only reason `Copy` types implement `Clone` is for\n generics, not for using the `clone` method on a concrete type.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n 42u64.clone();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "clone_on_ref_ptr", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 702 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for usage of `.clone()` on a ref-counted pointer, (`Rc`, `Arc`, `rc::Weak`, or `sync::Weak`), and suggests calling Clone via unified\n function syntax instead (e.g., `Rc::clone(foo)`).\n\n **Why is this bad?** Calling '.clone()' on an Rc, Arc, or Weak\n can obscure the fact that only the pointer is being cloned, not the underlying\n data.\n\n **Example:**\n ```rust\n # use std::rc::Rc;\n let x = Rc::new(1);\n\n // Bad\n x.clone();\n\n // Good\n Rc::clone(&x);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "clone_double_ref", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 724 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for usage of `.clone()` on an `&&T`.\n **Why is this bad?** Cloning an `&&T` copies the inner `&T`, instead of\n cloning the underlying `T`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn main() {\n let x = vec![1];\n let y = &&x;\n let z = y.clone();\n println!(\"{:p} {:p}\", *y, z); // prints out the same pointer\n }\n ```\n", + "applicability": { + "is_multi_suggestion": true, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "inefficient_to_string", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 747 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for usage of `.to_string()` on an `&&T` where `T` implements `ToString` directly (like `&&str` or `&&String`).\n\n **Why is this bad?** This bypasses the specialized implementation of\n `ToString` and instead goes through the more expensive string formatting\n facilities.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Generic implementation for `T: Display` is used (slow)\n [\"foo\", \"bar\"].iter().map(|s| s.to_string());\n\n // OK, the specialized impl is used\n [\"foo\", \"bar\"].iter().map(|&s| s.to_string());\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "new_ret_no_self", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 808 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `new` not returning a type that contains `Self`.\n **Why is this bad?** As a convention, `new` methods are used to make a new\n instance of a type.\n\n **Known problems:** None.\n\n **Example:**\n In an impl block:\n ```rust\n # struct Foo;\n # struct NotAFoo;\n impl Foo {\n fn new() -> NotAFoo {\n # NotAFoo\n }\n }\n ```\n\n ```rust\n # struct Foo;\n struct Bar(Foo);\n impl Foo {\n // Bad. The type name must contain `Self`\n fn new() -> Bar {\n # Bar(Foo)\n }\n }\n ```\n\n ```rust\n # struct Foo;\n # struct FooError;\n impl Foo {\n // Good. Return type contains `Self`\n fn new() -> Result {\n # Ok(Foo)\n }\n }\n ```\n\n Or in a trait definition:\n ```rust\n pub trait Trait {\n // Bad. The type name must contain `Self`\n fn new();\n }\n ```\n\n ```rust\n pub trait Trait {\n // Good. Return type contains `Self`\n fn new() -> Self;\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "single_char_pattern", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 829 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for string methods that receive a single-character `str` as an argument, e.g., `_.split(\"x\")`.\n\n **Why is this bad?** Performing these methods using a `char` is faster than\n using a `str`.\n\n **Known problems:** Does not catch multi-byte unicode characters.\n\n **Example:**\n ```rust,ignore\n // Bad\n _.split(\"x\");\n\n // Good\n _.split('x');\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "iterator_step_by_zero", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 848 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for calling `.step_by(0)` on iterators which panics.\n **Why is this bad?** This very much looks like an oversight. Use `panic!()` instead if you\n actually intend to panic.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,should_panic\n for x in (0..100).step_by(0) {\n //..\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "iter_nth_zero", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 876 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for the use of `iter.nth(0)`.\n **Why is this bad?** `iter.next()` is equivalent to\n `iter.nth(0)`, as they both consume the next element,\n but is more readable.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # use std::collections::HashSet;\n // Bad\n # let mut s = HashSet::new();\n # s.insert(1);\n let x = s.iter().nth(0);\n\n // Good\n # let mut s = HashSet::new();\n # s.insert(1);\n let x = s.iter().next();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "iter_nth", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 902 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for use of `.iter().nth()` (and the related `.iter_mut().nth()`) on standard library types with O(1) element access.\n\n **Why is this bad?** `.get()` and `.get_mut()` are more efficient and more\n readable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let some_vec = vec![0, 1, 2, 3];\n let bad_vec = some_vec.iter().nth(3);\n let bad_slice = &some_vec[..].iter().nth(3);\n ```\n The correct use would be:\n ```rust\n let some_vec = vec![0, 1, 2, 3];\n let bad_vec = some_vec.get(3);\n let bad_slice = &some_vec[..].get(3);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "iter_skip_next", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 926 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for use of `.skip(x).next()` on iterators.\n **Why is this bad?** `.nth(x)` is cleaner\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let some_vec = vec![0, 1, 2, 3];\n let bad_vec = some_vec.iter().skip(3).next();\n let bad_slice = &some_vec[..].iter().skip(3).next();\n ```\n The correct use would be:\n ```rust\n let some_vec = vec![0, 1, 2, 3];\n let bad_vec = some_vec.iter().nth(3);\n let bad_slice = &some_vec[..].iter().nth(3);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "get_unwrap", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 959 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for use of `.get().unwrap()` (or `.get_mut().unwrap`) on a standard library type which implements `Index`\n\n **Why is this bad?** Using the Index trait (`[]`) is more clear and more\n concise.\n\n **Known problems:** Not a replacement for error handling: Using either\n `.unwrap()` or the Index trait (`[]`) carries the risk of causing a `panic`\n if the value being accessed is `None`. If the use of `.get().unwrap()` is a\n temporary placeholder for dealing with the `Option` type, then this does\n not mitigate the need for error handling. If there is a chance that `.get()`\n will be `None` in your program, then it is advisable that the `None` case\n is handled in a future refactor instead of using `.unwrap()` or the Index\n trait.\n\n **Example:**\n ```rust\n let mut some_vec = vec![0, 1, 2, 3];\n let last = some_vec.get(3).unwrap();\n *some_vec.get_mut(0).unwrap() = 1;\n ```\n The correct use would be:\n ```rust\n let mut some_vec = vec![0, 1, 2, 3];\n let last = some_vec[3];\n some_vec[0] = 1;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "string_extend_chars", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 988 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for the use of `.extend(s.chars())` where s is a `&str` or `String`.\n\n **Why is this bad?** `.push_str(s)` is clearer\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let abc = \"abc\";\n let def = String::from(\"def\");\n let mut s = String::new();\n s.extend(abc.chars());\n s.extend(def.chars());\n ```\n The correct use would be:\n ```rust\n let abc = \"abc\";\n let def = String::from(\"def\");\n let mut s = String::new();\n s.push_str(abc);\n s.push_str(&def);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "iter_cloned_collect", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1011 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for the use of `.cloned().collect()` on slice to create a `Vec`.\n\n **Why is this bad?** `.to_vec()` is clearer\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let s = [1, 2, 3, 4, 5];\n let s2: Vec = s[..].iter().cloned().collect();\n ```\n The better use would be:\n ```rust\n let s = [1, 2, 3, 4, 5];\n let s2: Vec = s.to_vec();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "chars_last_cmp", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1035 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for usage of `_.chars().last()` or `_.chars().next_back()` on a `str` to check if it ends with a given char.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.ends_with(_)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let name = \"_\";\n\n // Bad\n name.chars().last() == Some('_') || name.chars().next_back() == Some('-');\n\n // Good\n name.ends_with('_') || name.ends_with('-');\n ```\n", + "applicability": null + }, + { + "id": "useless_asref", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1060 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `.as_ref()` or `.as_mut()` where the types before and after the call are the same.\n\n **Why is this bad?** The call is unnecessary.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn do_stuff(x: &[i32]) {}\n let x: &[i32] = &[1, 2, 3, 4, 5];\n do_stuff(x.as_ref());\n ```\n The correct use would be:\n ```rust\n # fn do_stuff(x: &[i32]) {}\n let x: &[i32] = &[1, 2, 3, 4, 5];\n do_stuff(x);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "unnecessary_fold", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1082 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for using `fold` when a more succinct alternative exists. Specifically, this checks for `fold`s which could be replaced by `any`, `all`,\n `sum` or `product`.\n\n **Why is this bad?** Readability.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let _ = (0..3).fold(false, |acc, x| acc || x > 2);\n ```\n This could be written as:\n ```rust\n let _ = (0..3).any(|x| x > 2);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "unnecessary_filter_map", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1111 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for `filter_map` calls which could be replaced by `filter` or `map`. More specifically it checks if the closure provided is only performing one of the\n filter or map operations and suggests the appropriate option.\n\n **Why is this bad?** Complexity. The intent is also clearer if only a single\n operation is being performed.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n let _ = (0..3).filter_map(|x| if x > 2 { Some(x) } else { None });\n\n // As there is no transformation of the argument this could be written as:\n let _ = (0..3).filter(|&x| x > 2);\n ```\n\n ```rust\n let _ = (0..4).filter_map(|x| Some(x + 1));\n\n // As there is no conditional check on the argument this could be written as:\n let _ = (0..4).map(|x| x + 1);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "into_iter_on_ref", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1135 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `into_iter` calls on references which should be replaced by `iter` or `iter_mut`.\n\n **Why is this bad?** Readability. Calling `into_iter` on a reference will not move out its\n content into the resulting iterator, which is confusing. It is better just call `iter` or\n `iter_mut` directly.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n // Bad\n let _ = (&vec![3, 4, 5]).into_iter();\n\n // Good\n let _ = (&vec![3, 4, 5]).iter();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "suspicious_map", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1154 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for calls to `map` followed by a `count`.\n **Why is this bad?** It looks suspicious. Maybe `map` was confused with `filter`.\n If the `map` call is intentional, this should be rewritten. Or, if you intend to\n drive the iterator to completion, you can just use `for_each` instead.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n let _ = (0..3).map(|x| x + 2).count();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "uninit_assumed_init", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1186 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for `MaybeUninit::uninit().assume_init()`.\n **Why is this bad?** For most types, this is undefined behavior.\n\n **Known problems:** For now, we accept empty tuples and tuples / arrays\n of `MaybeUninit`. There may be other types that allow uninitialized\n data, but those are not yet rigorously defined.\n\n **Example:**\n\n ```rust\n // Beware the UB\n use std::mem::MaybeUninit;\n\n let _: usize = unsafe { MaybeUninit::uninit().assume_init() };\n ```\n\n Note that the following is OK:\n\n ```rust\n use std::mem::MaybeUninit;\n\n let _: [MaybeUninit; 5] = unsafe {\n MaybeUninit::uninit().assume_init()\n };\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "manual_saturating_arithmetic", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1213 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`.\n **Why is this bad?** These can be written simply with `saturating_add/sub` methods.\n\n **Example:**\n\n ```rust\n # let y: u32 = 0;\n # let x: u32 = 100;\n let add = x.checked_add(y).unwrap_or(u32::MAX);\n let sub = x.checked_sub(y).unwrap_or(u32::MIN);\n ```\n\n can be written using dedicated methods for saturating addition/subtraction as:\n\n ```rust\n # let y: u32 = 0;\n # let x: u32 = 100;\n let add = x.saturating_add(y);\n let sub = x.saturating_sub(y);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "zst_offset", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1230 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to zero-sized types\n\n **Why is this bad?** This is a no-op, and likely unintended\n\n **Known problems:** None\n\n **Example:**\n ```rust\n unsafe { (&() as *const ()).offset(1) };\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "filetype_is_file", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1270 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for `FileType::is_file()`.\n **Why is this bad?** When people testing a file type with `FileType::is_file`\n they are testing whether a path is something they can get bytes from. But\n `is_file` doesn't cover special file types in unix-like systems, and doesn't cover\n symlink in windows. Using `!FileType::is_dir()` is a better way to that intention.\n\n **Example:**\n\n ```rust\n # || {\n let metadata = std::fs::metadata(\"foo.txt\")?;\n let filetype = metadata.file_type();\n\n if filetype.is_file() {\n // read file\n }\n # Ok::<_, std::io::Error>(())\n # };\n ```\n\n should be written as:\n\n ```rust\n # || {\n let metadata = std::fs::metadata(\"foo.txt\")?;\n let filetype = metadata.file_type();\n\n if !filetype.is_dir() {\n // read file\n }\n # Ok::<_, std::io::Error>(())\n # };\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "option_as_ref_deref", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1295 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str).\n **Why is this bad?** Readability, this can be written more concisely as\n `_.as_deref()`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let opt = Some(\"\".to_string());\n opt.as_ref().map(String::as_str)\n # ;\n ```\n Can be written as\n ```rust\n # let opt = Some(\"\".to_string());\n opt.as_deref()\n # ;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "iter_next_slice", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1321 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for usage of `iter().next()` on a Slice or an Array\n **Why is this bad?** These can be shortened into `.get()`\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let a = [1, 2, 3];\n # let b = vec![1, 2, 3];\n a[2..].iter().next();\n b.iter().next();\n ```\n should be written as:\n ```rust\n # let a = [1, 2, 3];\n # let b = vec![1, 2, 3];\n a.get(2);\n b.get(0);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "single_char_add_str", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1346 + }, + "group": "clippy::style", + "docs": " **What it does:** Warns when using `push_str`/`insert_str` with a single-character string literal where `push`/`insert` with a `char` would work fine.\n\n **Why is this bad?** It's less clear that we are pushing a single character.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n let mut string = String::new();\n string.insert_str(0, \"R\");\n string.push_str(\"R\");\n ```\n Could be written as\n ```rust\n let mut string = String::new();\n string.insert(0, 'R');\n string.push('R');\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "unnecessary_lazy_evaluations", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1382 + }, + "group": "clippy::style", + "docs": " **What it does:** As the counterpart to `or_fun_call`, this lint looks for unnecessary lazily evaluated closures on `Option` and `Result`.\n\n This lint suggests changing the following functions, when eager evaluation results in\n simpler code:\n - `unwrap_or_else` to `unwrap_or`\n - `and_then` to `and`\n - `or_else` to `or`\n - `get_or_insert_with` to `get_or_insert`\n - `ok_or_else` to `ok_or`\n\n **Why is this bad?** Using eager evaluation is shorter and simpler in some cases.\n\n **Known problems:** It is possible, but not recommended for `Deref` and `Index` to have\n side effects. Eagerly evaluating them can change the semantics of the program.\n\n **Example:**\n\n ```rust\n // example code where clippy issues a warning\n let opt: Option = None;\n\n opt.unwrap_or_else(|| 42);\n ```\n Use instead:\n ```rust\n let opt: Option = None;\n\n opt.unwrap_or(42);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "map_collect_result_unit", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1403 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for usage of `_.map(_).collect::()`.\n **Why is this bad?** Using `try_for_each` instead is more readable and idiomatic.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n (0..3).map(|t| Err(t)).collect::>();\n ```\n Use instead:\n ```rust\n (0..3).try_for_each(|t| Err(t));\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "from_iter_instead_of_collect", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1436 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `from_iter()` function calls on types that implement the `FromIterator` trait.\n\n **Why is this bad?** It is recommended style to use collect. See\n [FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html)\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n use std::iter::FromIterator;\n\n let five_fives = std::iter::repeat(5).take(5);\n\n let v = Vec::from_iter(five_fives);\n\n assert_eq!(v, vec![5, 5, 5, 5, 5]);\n ```\n Use instead:\n ```rust\n let five_fives = std::iter::repeat(5).take(5);\n\n let v: Vec = five_fives.collect();\n\n assert_eq!(v, vec![5, 5, 5, 5, 5]);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "inspect_for_each", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1466 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `inspect().for_each()`.\n **Why is this bad?** It is the same as performing the computation\n inside `inspect` at the beginning of the closure in `for_each`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n [1,2,3,4,5].iter()\n .inspect(|&x| println!(\"inspect the number: {}\", x))\n .for_each(|&x| {\n assert!(x >= 0);\n });\n ```\n Can be written as\n ```rust\n [1,2,3,4,5].iter()\n .for_each(|&x| {\n println!(\"inspect the number: {}\", x);\n assert!(x >= 0);\n });\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "filter_map_identity", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1489 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `filter_map(|x| x)`.\n **Why is this bad?** Readability, this can be written more concisely by using `flatten`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # let iter = vec![Some(1)].into_iter();\n iter.filter_map(|x| x);\n ```\n Use instead:\n ```rust\n # let iter = vec![Some(1)].into_iter();\n iter.flatten();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "bytes_nth", + "id_span": { + "path": "clippy_lints/src/methods/mod.rs", + "line": 1511 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for the use of `.bytes().nth()`.\n **Why is this bad?** `.as_bytes().get()` is more efficient and more\n readable.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let _ = \"Hello\".bytes().nth(3);\n\n // Good\n let _ = \"Hello\".as_bytes().get(3);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "min_max", + "id_span": { + "path": "clippy_lints/src/minmax.rs", + "line": 28 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for expressions where `std::cmp::min` and `max` are used to clamp values, but switched so that the result is constant.\n\n **Why is this bad?** This is in all probability not the intended outcome. At\n the least it hurts readability of the code.\n\n **Known problems:** None\n\n **Example:**\n ```ignore\n min(0, max(100, x))\n ```\n or\n ```ignore\n x.max(100).min(0)\n ```\n It will always be equal to `0`. Probably the author meant to clamp the value\n between 0 and 100, but has erroneously swapped `min` and `max`.\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "toplevel_ref_arg", + "id_span": { + "path": "clippy_lints/src/misc.rs", + "line": 53 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for function arguments and let bindings denoted as `ref`.\n\n **Why is this bad?** The `ref` declaration makes the function take an owned\n value, but turns the argument into a reference (which means that the value\n is destroyed when exiting the function). This adds not much value: either\n take a reference type, or take an owned value and create references in the\n body.\n\n For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The\n type of `x` is more obvious with the former.\n\n **Known problems:** If the argument is dereferenced within the function,\n removing the `ref` will lead to errors. This can be fixed by removing the\n dereferences, e.g., changing `*x` to `x` within the function.\n\n **Example:**\n ```rust,ignore\n // Bad\n fn foo(ref x: u8) -> bool {\n true\n }\n\n // Good\n fn foo(x: &u8) -> bool {\n true\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "cmp_nan", + "id_span": { + "path": "clippy_lints/src/misc.rs", + "line": 76 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for comparisons to NaN.\n **Why is this bad?** NaN does not compare meaningfully to anything – not\n even itself – so those comparisons are simply wrong.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = 1.0;\n\n // Bad\n if x == f32::NAN { }\n\n // Good\n if x.is_nan() { }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "float_cmp", + "id_span": { + "path": "clippy_lints/src/misc.rs", + "line": 109 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for (in-)equality comparisons on floating-point values (apart from zero), except in functions called `*eq*` (which probably\n implement equality for a type involving floats).\n\n **Why is this bad?** Floating point calculations are usually imprecise, so\n asking if two values are *exactly* equal is asking for trouble. For a good\n guide on what to do, see [the floating point\n guide](http://www.floating-point-gui.de/errors/comparison).\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = 1.2331f64;\n let y = 1.2332f64;\n\n // Bad\n if y == 1.23f64 { }\n if y != x {} // where both are floats\n\n // Good\n let error_margin = f64::EPSILON; // Use an epsilon for comparison\n // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.\n // let error_margin = std::f64::EPSILON;\n if (y - 1.23f64).abs() < error_margin { }\n if (y - x).abs() > error_margin { }\n ```\n", + "applicability": null + }, + { + "id": "cmp_owned", + "id_span": { + "path": "clippy_lints/src/misc.rs", + "line": 136 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for conversions to owned values just for the sake of a comparison.\n\n **Why is this bad?** The comparison can operate on a reference, so creating\n an owned value effectively throws it away directly afterwards, which is\n needlessly consuming code and heap space.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = \"foo\";\n # let y = String::from(\"foo\");\n if x.to_owned() == y {}\n ```\n Could be written as\n ```rust\n # let x = \"foo\";\n # let y = String::from(\"foo\");\n if x == y {}\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "modulo_one", + "id_span": { + "path": "clippy_lints/src/misc.rs", + "line": 159 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for getting the remainder of a division by one or minus one.\n\n **Why is this bad?** The result for a divisor of one can only ever be zero; for\n minus one it can cause panic/overflow (if the left operand is the minimal value of\n the respective integer type) or results in zero. No one will write such code\n deliberately, unless trying to win an Underhanded Rust Contest. Even for that\n contest, it's probably a bad idea. Use something more underhanded.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = 1;\n let a = x % 1;\n let a = x % -1;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "used_underscore_binding", + "id_span": { + "path": "clippy_lints/src/misc.rs", + "line": 181 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for the use of bindings with a single leading underscore.\n\n **Why is this bad?** A single leading underscore is usually used to indicate\n that a binding will not be used. Using such a binding breaks this\n expectation.\n\n **Known problems:** The lint does not work properly with desugaring and\n macro, it has been allowed in the mean time.\n\n **Example:**\n ```rust\n let _x = 0;\n let y = _x + 1; // Here we are using `_x`, even though it has a leading\n // underscore. We should rename `_x` to `x`\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "short_circuit_statement", + "id_span": { + "path": "clippy_lints/src/misc.rs", + "line": 201 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for the use of short circuit boolean conditions as a\n statement.\n\n **Why is this bad?** Using a short circuit boolean condition as a statement\n may hide the fact that the second part is executed or not depending on the\n outcome of the first part.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n f() && g(); // We should write `if f() { g(); }`.\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "zero_ptr", + "id_span": { + "path": "clippy_lints/src/misc.rs", + "line": 223 + }, + "group": "clippy::style", + "docs": " **What it does:** Catch casts from `0` to some pointer type\n **Why is this bad?** This generally means `null` and is better expressed as\n {`std`, `core`}`::ptr::`{`null`, `null_mut`}.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let a = 0 as *const u32;\n\n // Good\n let a = std::ptr::null::();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "float_cmp_const", + "id_span": { + "path": "clippy_lints/src/misc.rs", + "line": 254 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for (in-)equality comparisons on floating-point value and constant, except in functions called `*eq*` (which probably\n implement equality for a type involving floats).\n\n **Why is this bad?** Floating point calculations are usually imprecise, so\n asking if two values are *exactly* equal is asking for trouble. For a good\n guide on what to do, see [the floating point\n guide](http://www.floating-point-gui.de/errors/comparison).\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x: f64 = 1.0;\n const ONE: f64 = 1.00;\n\n // Bad\n if x == ONE { } // where both are floats\n\n // Good\n let error_margin = f64::EPSILON; // Use an epsilon for comparison\n // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.\n // let error_margin = std::f64::EPSILON;\n if (x - ONE).abs() < error_margin { }\n ```\n", + "applicability": null + }, + { + "id": "unneeded_field_pattern", + "id_span": { + "path": "clippy_lints/src/misc_early.rs", + "line": 44 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for structure field patterns bound to wildcards.\n **Why is this bad?** Using `..` instead is shorter and leaves the focus on\n the fields that are actually bound.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct Foo {\n # a: i32,\n # b: i32,\n # c: i32,\n # }\n let f = Foo { a: 0, b: 0, c: 0 };\n\n // Bad\n match f {\n Foo { a: _, b: 0, .. } => {},\n Foo { a: _, b: _, c: _ } => {},\n }\n\n // Good\n match f {\n Foo { b: 0, .. } => {},\n Foo { .. } => {},\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "duplicate_underscore_argument", + "id_span": { + "path": "clippy_lints/src/misc_early.rs", + "line": 65 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for function arguments having the similar names differing by an underscore.\n\n **Why is this bad?** It affects code readability.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n fn foo(a: i32, _a: i32) {}\n\n // Good\n fn bar(a: i32, _b: i32) {}\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "double_neg", + "id_span": { + "path": "clippy_lints/src/misc_early.rs", + "line": 83 + }, + "group": "clippy::style", + "docs": " **What it does:** Detects expressions of the form `--x`.\n **Why is this bad?** It can mislead C/C++ programmers to think `x` was\n decremented.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let mut x = 3;\n --x;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "mixed_case_hex_literals", + "id_span": { + "path": "clippy_lints/src/misc_early.rs", + "line": 104 + }, + "group": "clippy::style", + "docs": " **What it does:** Warns on hexadecimal literals with mixed-case letter digits.\n\n **Why is this bad?** It looks confusing.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n let y = 0x1a9BAcD;\n\n // Good\n let y = 0x1A9BACD;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "unseparated_literal_suffix", + "id_span": { + "path": "clippy_lints/src/misc_early.rs", + "line": 125 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Warns if literal suffixes are not separated by an underscore.\n\n **Why is this bad?** It is much less readable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n let y = 123832i32;\n\n // Good\n let y = 123832_i32;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "zero_prefixed_literal", + "id_span": { + "path": "clippy_lints/src/misc_early.rs", + "line": 163 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Warns if an integral constant literal starts with `0`.\n **Why is this bad?** In some languages (including the infamous C language\n and most of its\n family), this marks an octal constant. In Rust however, this is a decimal\n constant. This could\n be confusing for both the writer and a reader of the constant.\n\n **Known problems:** None.\n\n **Example:**\n\n In Rust:\n ```rust\n fn main() {\n let a = 0123;\n println!(\"{}\", a);\n }\n ```\n\n prints `123`, while in C:\n\n ```c\n #include \n\n int main() {\n int a = 0123;\n printf(\"%d\\n\", a);\n }\n ```\n\n prints `83` (as `83 == 0o123` while `123 == 0o173`).\n", + "applicability": { + "is_multi_suggestion": true, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "builtin_type_shadow", + "id_span": { + "path": "clippy_lints/src/misc_early.rs", + "line": 184 + }, + "group": "clippy::style", + "docs": " **What it does:** Warns if a generic shadows a built-in type.\n **Why is this bad?** This gives surprising type errors.\n\n **Known problems:** None.\n\n **Example:**\n\n ```ignore\n impl Foo {\n fn impl_func(&self) -> u32 {\n 42\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "redundant_pattern", + "id_span": { + "path": "clippy_lints/src/misc_early.rs", + "line": 213 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for patterns in the form `name @ _`.\n **Why is this bad?** It's almost always more readable to just use direct\n bindings.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let v = Some(\"abc\");\n\n // Bad\n match v {\n Some(x) => (),\n y @ _ => (),\n }\n\n // Good\n match v {\n Some(x) => (),\n y => (),\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "unneeded_wildcard_pattern", + "id_span": { + "path": "clippy_lints/src/misc_early.rs", + "line": 247 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`).\n\n _NOTE_: While `_, ..` means there is at least one element left, `..`\n means there are 0 or more elements left. This can make a difference\n when refactoring, but shouldn't result in errors in the refactored code,\n since the wildcard pattern isn't used anyway.\n **Why is this bad?** The wildcard pattern is unneeded as the rest pattern\n can match that element as well.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct TupleStruct(u32, u32, u32);\n # let t = TupleStruct(1, 2, 3);\n // Bad\n match t {\n TupleStruct(0, .., _) => (),\n _ => (),\n }\n\n // Good\n match t {\n TupleStruct(0, ..) => (),\n _ => (),\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "missing_const_for_fn", + "id_span": { + "path": "clippy_lints/src/missing_const_for_fn.rs", + "line": 72 + }, + "group": "clippy::nursery", + "docs": " **What it does:**\n Suggests the use of `const` in functions and methods where possible.\n\n **Why is this bad?**\n\n Not having the function const prevents callers of the function from being const as well.\n\n **Known problems:**\n\n Const functions are currently still being worked on, with some features only being available\n on nightly. This lint does not consider all edge cases currently and the suggestions may be\n incorrect if you are using this lint on stable.\n\n Also, the lint only runs one pass over the code. Consider these two non-const functions:\n\n ```rust\n fn a() -> i32 {\n 0\n }\n fn b() -> i32 {\n a()\n }\n ```\n\n When running Clippy, the lint will only suggest to make `a` const, because `b` at this time\n can't be const as it calls a non-const function. Making `a` const and running Clippy again,\n will suggest to make `b` const, too.\n\n **Example:**\n\n ```rust\n # struct Foo {\n # random_number: usize,\n # }\n # impl Foo {\n fn new() -> Self {\n Self { random_number: 42 }\n }\n # }\n ```\n\n Could be a const fn:\n\n ```rust\n # struct Foo {\n # random_number: usize,\n # }\n # impl Foo {\n const fn new() -> Self {\n Self { random_number: 42 }\n }\n # }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "missing_docs_in_private_items", + "id_span": { + "path": "clippy_lints/src/missing_doc.rs", + "line": 29 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Warns if there is missing doc for any documentable item (public or private).\n\n **Why is this bad?** Doc is good. *rustc* has a `MISSING_DOCS`\n allowed-by-default lint for\n public members, but has no way to enforce documentation of private items.\n This lint fixes that.\n\n **Known problems:** None.\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "missing_inline_in_public_items", + "id_span": { + "path": "clippy_lints/src/missing_inline.rs", + "line": 55 + }, + "group": "clippy::restriction", + "docs": " **What it does:** it lints if an exported function, method, trait method with default impl, or trait method impl is not `#[inline]`.\n\n **Why is this bad?** In general, it is not. Functions can be inlined across\n crates when that's profitable as long as any form of LTO is used. When LTO is disabled,\n functions that are not `#[inline]` cannot be inlined across crates. Certain types of crates\n might intend for most of the methods in their public API to be able to be inlined across\n crates even when LTO is disabled. For these types of crates, enabling this lint might make\n sense. It allows the crate to require all exported methods to be `#[inline]` by default, and\n then opt out for specific methods where this might not make sense.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n pub fn foo() {} // missing #[inline]\n fn ok() {} // ok\n #[inline] pub fn bar() {} // ok\n #[inline(always)] pub fn baz() {} // ok\n\n pub trait Bar {\n fn bar(); // ok\n fn def_bar() {} // missing #[inline]\n }\n\n struct Baz;\n impl Baz {\n fn private() {} // ok\n }\n\n impl Bar for Baz {\n fn bar() {} // ok - Baz is not exported\n }\n\n pub struct PubBaz;\n impl PubBaz {\n fn private() {} // ok\n pub fn not_ptrivate() {} // missing #[inline]\n }\n\n impl Bar for PubBaz {\n fn bar() {} // missing #[inline]\n fn def_bar() {} // missing #[inline]\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "modulo_arithmetic", + "id_span": { + "path": "clippy_lints/src/modulo_arithmetic.rs", + "line": 26 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for modulo arithmetic.\n **Why is this bad?** The results of modulo (%) operation might differ\n depending on the language, when negative numbers are involved.\n If you interop with different languages it might be beneficial\n to double check all places that use modulo arithmetic.\n\n For example, in Rust `17 % -3 = 2`, but in Python `17 % -3 = -1`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = -17 % 3;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "multiple_crate_versions", + "id_span": { + "path": "clippy_lints/src/multiple_crate_versions.rs", + "line": 32 + }, + "group": "clippy::cargo", + "docs": " **What it does:** Checks to see if multiple versions of a crate are being used.\n\n **Why is this bad?** This bloats the size of targets, and can lead to\n confusing error messages when structs or traits are used interchangeably\n between different versions of a crate.\n\n **Known problems:** Because this can be caused purely by the dependencies\n themselves, it's not always possible to fix this issue.\n\n **Example:**\n ```toml\n # This will pull in both winapi v0.3.x and v0.2.x, triggering a warning.\n [dependencies]\n ctrlc = \"=3.1.0\"\n ansi_term = \"=0.11.0\"\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "mutable_key_type", + "id_span": { + "path": "clippy_lints/src/mut_key.rs", + "line": 50 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for sets/maps with mutable key types.\n **Why is this bad?** All of `HashMap`, `HashSet`, `BTreeMap` and\n `BtreeSet` rely on either the hash or the order of keys be unchanging,\n so having types with interior mutability is a bad idea.\n\n **Known problems:** It's correct to use a struct, that contains interior mutability\n as a key, when its `Hash` implementation doesn't access any of the interior mutable types.\n However, this lint is unable to recognize this, so it causes a false positive in theses cases.\n The `bytes` crate is a great example of this.\n\n **Example:**\n ```rust\n use std::cmp::{PartialEq, Eq};\n use std::collections::HashSet;\n use std::hash::{Hash, Hasher};\n use std::sync::atomic::AtomicUsize;\n# #[allow(unused)]\n\n struct Bad(AtomicUsize);\n impl PartialEq for Bad {\n fn eq(&self, rhs: &Self) -> bool {\n ..\n ; unimplemented!();\n }\n }\n\n impl Eq for Bad {}\n\n impl Hash for Bad {\n fn hash(&self, h: &mut H) {\n ..\n ; unimplemented!();\n }\n }\n\n fn main() {\n let _: HashSet = HashSet::new();\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "mut_mut", + "id_span": { + "path": "clippy_lints/src/mut_mut.rs", + "line": 24 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for instances of `mut mut` references.\n **Why is this bad?** Multiple `mut`s don't add anything meaningful to the\n source. This is either a copy'n'paste error, or it shows a fundamental\n misunderstanding of references.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let mut y = 1;\n let x = &mut &mut y;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "mut_mutex_lock", + "id_span": { + "path": "clippy_lints/src/mut_mutex_lock.rs", + "line": 40 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `&mut Mutex::lock` calls\n **Why is this bad?** `Mutex::lock` is less efficient than\n calling `Mutex::get_mut`. In addition you also have a statically\n guarantee that the mutex isn't locked, instead of just a runtime\n guarantee.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n use std::sync::{Arc, Mutex};\n\n let mut value_rc = Arc::new(Mutex::new(42_u8));\n let value_mutex = Arc::get_mut(&mut value_rc).unwrap();\n\n let mut value = value_mutex.lock().unwrap();\n *value += 1;\n ```\n Use instead:\n ```rust\n use std::sync::{Arc, Mutex};\n\n let mut value_rc = Arc::new(Mutex::new(42_u8));\n let value_mutex = Arc::get_mut(&mut value_rc).unwrap();\n\n let value = value_mutex.get_mut().unwrap();\n *value += 1;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "unnecessary_mut_passed", + "id_span": { + "path": "clippy_lints/src/mut_reference.rs", + "line": 25 + }, + "group": "clippy::style", + "docs": " **What it does:** Detects passing a mutable reference to a function that only requires an immutable reference.\n\n **Why is this bad?** The mutable reference rules out all other references to\n the value. Also the code misleads about the intent of the call site.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n // Bad\n my_vec.push(&mut value)\n\n // Good\n my_vec.push(&value)\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "debug_assert_with_mut_call", + "id_span": { + "path": "clippy_lints/src/mutable_debug_assertion.rs", + "line": 28 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Checks for function/method calls with a mutable parameter in `debug_assert!`, `debug_assert_eq!` and `debug_assert_ne!` macros.\n\n **Why is this bad?** In release builds `debug_assert!` macros are optimized out by the\n compiler.\n Therefore mutating something in a `debug_assert!` macro results in different behaviour\n between a release and debug build.\n\n **Known problems:** None\n\n **Example:**\n ```rust,ignore\n debug_assert_eq!(vec![3].pop(), Some(3));\n // or\n fn take_a_mut_parameter(_: &mut u32) -> bool { unimplemented!() }\n debug_assert!(take_a_mut_parameter(&mut 5));\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "mutex_atomic", + "id_span": { + "path": "clippy_lints/src/mutex_atomic.rs", + "line": 34 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for usages of `Mutex` where an atomic will do.\n **Why is this bad?** Using a mutex just to make access to a plain bool or\n reference sequential is shooting flies with cannons.\n `std::sync::atomic::AtomicBool` and `std::sync::atomic::AtomicPtr` are leaner and\n faster.\n\n **Known problems:** This lint cannot detect if the mutex is actually used\n for waiting before a critical section.\n\n **Example:**\n ```rust\n # let y = true;\n\n // Bad\n # use std::sync::Mutex;\n let x = Mutex::new(&y);\n\n // Good\n # use std::sync::atomic::AtomicBool;\n let x = AtomicBool::new(y);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "mutex_integer", + "id_span": { + "path": "clippy_lints/src/mutex_atomic.rs", + "line": 59 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Checks for usages of `Mutex` where `X` is an integral type.\n\n **Why is this bad?** Using a mutex just to make access to a plain integer\n sequential is\n shooting flies with cannons. `std::sync::atomic::AtomicUsize` is leaner and faster.\n\n **Known problems:** This lint cannot detect if the mutex is actually used\n for waiting before a critical section.\n\n **Example:**\n ```rust\n # use std::sync::Mutex;\n let x = Mutex::new(0usize);\n\n // Good\n # use std::sync::atomic::AtomicUsize;\n let x = AtomicUsize::new(0usize);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "needless_arbitrary_self_type", + "id_span": { + "path": "clippy_lints/src/needless_arbitrary_self_type.rs", + "line": 55 + }, + "group": "clippy::complexity", + "docs": " **What it does:** The lint checks for `self` in fn parameters that specify the `Self`-type explicitly\n **Why is this bad?** Increases the amount and decreases the readability of code\n\n **Known problems:** None\n\n **Example:**\n ```rust\n enum ValType {\n I32,\n I64,\n F32,\n F64,\n }\n\n impl ValType {\n pub fn bytes(self: Self) -> usize {\n match self {\n Self::I32 | Self::F32 => 4,\n Self::I64 | Self::F64 => 8,\n }\n }\n }\n ```\n\n Could be rewritten as\n\n ```rust\n enum ValType {\n I32,\n I64,\n F32,\n F64,\n }\n\n impl ValType {\n pub fn bytes(self) -> usize {\n match self {\n Self::I32 | Self::F32 => 4,\n Self::I64 | Self::F64 => 8,\n }\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "needless_bool", + "id_span": { + "path": "clippy_lints/src/needless_bool.rs", + "line": 38 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for expressions of the form `if c { true } else { false }` (or vice versa) and suggests using the condition directly.\n\n **Why is this bad?** Redundant code.\n\n **Known problems:** Maybe false positives: Sometimes, the two branches are\n painstakingly documented (which we, of course, do not detect), so they *may*\n have some value. Even then, the documentation can be rewritten to match the\n shorter code.\n\n **Example:**\n ```rust,ignore\n if x {\n false\n } else {\n true\n }\n ```\n Could be written as\n ```rust,ignore\n !x\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "bool_comparison", + "id_span": { + "path": "clippy_lints/src/needless_bool.rs", + "line": 62 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for expressions of the form `x == true`, `x != true` and order comparisons such as `x < true` (or vice versa) and\n suggest using the variable directly.\n\n **Why is this bad?** Unnecessary code.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n if x == true {}\n if y == false {}\n ```\n use `x` directly:\n ```rust,ignore\n if x {}\n if !y {}\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "needless_borrow", + "id_span": { + "path": "clippy_lints/src/needless_borrow.rs", + "line": 32 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Checks for address of operations (`&`) that are going to be dereferenced immediately by the compiler.\n\n **Why is this bad?** Suggests that the receiver of the expression borrows\n the expression.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n let x: &i32 = &&&&&&5;\n\n // Good\n let x: &i32 = &5;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "needless_borrowed_reference", + "id_span": { + "path": "clippy_lints/src/needless_borrowed_ref.rs", + "line": 48 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for useless borrowed references.\n **Why is this bad?** It is mostly useless and make the code look more\n complex than it\n actually is.\n\n **Known problems:** It seems that the `&ref` pattern is sometimes useful.\n For instance in the following snippet:\n ```rust,ignore\n enum Animal {\n Cat(u64),\n Dog(u64),\n }\n\n fn foo(a: &Animal, b: &Animal) {\n match (a, b) {\n (&Animal::Cat(v), k) | (k, &Animal::Cat(v)) => (), // lifetime mismatch error\n (&Animal::Dog(ref c), &Animal::Dog(_)) => ()\n }\n }\n ```\n There is a lifetime mismatch error for `k` (indeed a and b have distinct\n lifetime).\n This can be fixed by using the `&ref` pattern.\n However, the code can also be fixed by much cleaner ways\n\n **Example:**\n ```rust\n let mut v = Vec::::new();\n let _ = v.iter_mut().filter(|&ref a| a.is_empty());\n ```\n This closure takes a reference on something that has been matched as a\n reference and\n de-referenced.\n As such, it could just be |a| a.is_empty()\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "needless_continue", + "id_span": { + "path": "clippy_lints/src/needless_continue.rs", + "line": 114 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** The lint checks for `if`-statements appearing in loops that contain a `continue` statement in either their main blocks or their\n `else`-blocks, when omitting the `else`-block possibly with some\n rearrangement of code can make the code easier to understand.\n\n **Why is this bad?** Having explicit `else` blocks for `if` statements\n containing `continue` in their THEN branch adds unnecessary branching and\n nesting to the code. Having an else block containing just `continue` can\n also be better written by grouping the statements following the whole `if`\n statement within the THEN block and omitting the else block completely.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n # fn condition() -> bool { false }\n # fn update_condition() {}\n # let x = false;\n while condition() {\n update_condition();\n if x {\n // ...\n } else {\n continue;\n }\n println!(\"Hello, world\");\n }\n ```\n\n Could be rewritten as\n\n ```rust\n # fn condition() -> bool { false }\n # fn update_condition() {}\n # let x = false;\n while condition() {\n update_condition();\n if x {\n // ...\n println!(\"Hello, world\");\n }\n }\n ```\n\n As another example, the following code\n\n ```rust\n # fn waiting() -> bool { false }\n loop {\n if waiting() {\n continue;\n } else {\n // Do something useful\n }\n # break;\n }\n ```\n Could be rewritten as\n\n ```rust\n # fn waiting() -> bool { false }\n loop {\n if waiting() {\n continue;\n }\n // Do something useful\n # break;\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "needless_pass_by_value", + "id_span": { + "path": "clippy_lints/src/needless_pass_by_value.rs", + "line": 50 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for functions taking arguments by value, but not consuming them in its\n body.\n\n **Why is this bad?** Taking arguments by reference is more flexible and can\n sometimes avoid\n unnecessary allocations.\n\n **Known problems:**\n * This lint suggests taking an argument by reference,\n however sometimes it is better to let users decide the argument type\n (by using `Borrow` trait, for example), depending on how the function is used.\n\n **Example:**\n ```rust\n fn foo(v: Vec) {\n assert_eq!(v.len(), 42);\n }\n ```\n should be\n ```rust\n fn foo(v: &[i32]) {\n assert_eq!(v.len(), 42);\n }\n ```\n", + "applicability": { + "is_multi_suggestion": true, + "applicability": null + } + }, + { + "id": "needless_question_mark", + "id_span": { + "path": "clippy_lints/src/needless_question_mark.rs", + "line": 57 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Suggests alternatives for useless applications of `?` in terminating expressions\n\n **Why is this bad?** There's no reason to use `?` to short-circuit when execution of the body will end there anyway.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n struct TO {\n magic: Option,\n }\n\n fn f(to: TO) -> Option {\n Some(to.magic?)\n }\n\n struct TR {\n magic: Result,\n }\n\n fn g(tr: Result) -> Result {\n tr.and_then(|t| Ok(t.magic?))\n }\n\n ```\n Use instead:\n ```rust\n struct TO {\n magic: Option,\n }\n\n fn f(to: TO) -> Option {\n to.magic\n }\n\n struct TR {\n magic: Result,\n }\n\n fn g(tr: Result) -> Result {\n tr.and_then(|t| t.magic)\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "needless_update", + "id_span": { + "path": "clippy_lints/src/needless_update.rs", + "line": 43 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for needlessly including a base struct on update when all fields are changed anyway.\n\n This lint is not applied to structs marked with\n [non_exhaustive](https://doc.rust-lang.org/reference/attributes/type_system.html).\n\n **Why is this bad?** This will cost resources (because the base has to be\n somewhere), and make the code less readable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct Point {\n # x: i32,\n # y: i32,\n # z: i32,\n # }\n # let zero_point = Point { x: 0, y: 0, z: 0 };\n\n // Bad\n Point {\n x: 1,\n y: 1,\n z: 1,\n ..zero_point\n };\n\n // Ok\n Point {\n x: 1,\n y: 1,\n ..zero_point\n };\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "neg_cmp_op_on_partial_ord", + "id_span": { + "path": "clippy_lints/src/neg_cmp_op_on_partial_ord.rs", + "line": 41 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for the usage of negated comparison operators on types which only implement\n `PartialOrd` (e.g., `f64`).\n\n **Why is this bad?**\n These operators make it easy to forget that the underlying types actually allow not only three\n potential Orderings (Less, Equal, Greater) but also a fourth one (Uncomparable). This is\n especially easy to miss if the operator based comparison result is negated.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n use std::cmp::Ordering;\n\n // Bad\n let a = 1.0;\n let b = f64::NAN;\n\n let _not_less_or_equal = !(a <= b);\n\n // Good\n let a = 1.0;\n let b = f64::NAN;\n\n let _not_less_or_equal = match a.partial_cmp(&b) {\n None | Some(Ordering::Greater) => true,\n _ => false,\n };\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "neg_multiply", + "id_span": { + "path": "clippy_lints/src/neg_multiply.rs", + "line": 21 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for multiplication by -1 as a form of negation.\n **Why is this bad?** It's more readable to just negate.\n\n **Known problems:** This only catches integers (for now).\n\n **Example:**\n ```ignore\n x * -1\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "new_without_default", + "id_span": { + "path": "clippy_lints/src/new_without_default.rs", + "line": 48 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for types with a `fn new() -> Self` method and no implementation of\n [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html).\n\n **Why is this bad?** The user might expect to be able to use\n [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) as the\n type can be constructed without arguments.\n\n **Known problems:** Hopefully none.\n\n **Example:**\n\n ```ignore\n struct Foo(Bar);\n\n impl Foo {\n fn new() -> Self {\n Foo(Bar::new())\n }\n }\n ```\n\n To fix the lint, add a `Default` implementation that delegates to `new`:\n\n ```ignore\n struct Foo(Bar);\n\n impl Default for Foo {\n fn default() -> Self {\n Foo::new()\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "no_effect", + "id_span": { + "path": "clippy_lints/src/no_effect.rs", + "line": 22 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for statements which have no effect.\n **Why is this bad?** Similar to dead code, these statements are actually\n executed. However, as they have no effect, all they do is make the code less\n readable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n 0;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "unnecessary_operation", + "id_span": { + "path": "clippy_lints/src/no_effect.rs", + "line": 40 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for expression statements that can be reduced to a sub-expression.\n\n **Why is this bad?** Expressions by themselves often have no side-effects.\n Having such expressions reduces readability.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n compute_array()[0];\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "declare_interior_mutable_const", + "id_span": { + "path": "clippy_lints/src/non_copy_const.rs", + "line": 69 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for declaration of `const` items which is interior mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.).\n\n **Why is this bad?** Consts are copied everywhere they are referenced, i.e.,\n every time you refer to the const a fresh instance of the `Cell` or `Mutex`\n or `AtomicXxxx` will be created, which defeats the whole purpose of using\n these types in the first place.\n\n The `const` should better be replaced by a `static` item if a global\n variable is wanted, or replaced by a `const fn` if a constructor is wanted.\n\n **Known problems:** A \"non-constant\" const item is a legacy way to supply an\n initialized value to downstream `static` items (e.g., the\n `std::sync::ONCE_INIT` constant). In this case the use of `const` is legit,\n and this lint should be suppressed.\n\n Even though the lint avoids triggering on a constant whose type has enums that have variants\n with interior mutability, and its value uses non interior mutable variants (see\n [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) and\n [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) for examples);\n it complains about associated constants without default values only based on its types;\n which might not be preferable.\n There're other enums plus associated constants cases that the lint cannot handle.\n\n Types that have underlying or potential interior mutability trigger the lint whether\n the interior mutable field is used or not. See issues\n [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and\n\n **Example:**\n ```rust\n use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};\n\n // Bad.\n const CONST_ATOM: AtomicUsize = AtomicUsize::new(12);\n CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged\n assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct\n\n // Good.\n static STATIC_ATOM: AtomicUsize = AtomicUsize::new(15);\n STATIC_ATOM.store(9, SeqCst);\n assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance\n ```\n", + "applicability": null + }, + { + "id": "borrow_interior_mutable_const", + "id_span": { + "path": "clippy_lints/src/non_copy_const.rs", + "line": 110 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks if `const` items which is interior mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly.\n\n **Why is this bad?** Consts are copied everywhere they are referenced, i.e.,\n every time you refer to the const a fresh instance of the `Cell` or `Mutex`\n or `AtomicXxxx` will be created, which defeats the whole purpose of using\n these types in the first place.\n\n The `const` value should be stored inside a `static` item.\n\n **Known problems:** When an enum has variants with interior mutability, use of its non\n interior mutable variants can generate false positives. See issue\n [#3962](https://github.com/rust-lang/rust-clippy/issues/3962)\n\n Types that have underlying or potential interior mutability trigger the lint whether\n the interior mutable field is used or not. See issues\n [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and\n [#3825](https://github.com/rust-lang/rust-clippy/issues/3825)\n\n **Example:**\n ```rust\n use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};\n const CONST_ATOM: AtomicUsize = AtomicUsize::new(12);\n\n // Bad.\n CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged\n assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct\n\n // Good.\n static STATIC_ATOM: AtomicUsize = CONST_ATOM;\n STATIC_ATOM.store(9, SeqCst);\n assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance\n ```\n", + "applicability": null + }, + { + "id": "similar_names", + "id_span": { + "path": "clippy_lints/src/non_expressive_names.rs", + "line": 27 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for names that are very similar and thus confusing.\n **Why is this bad?** It's hard to distinguish between names that differ only\n by a single character.\n\n **Known problems:** None?\n\n **Example:**\n ```ignore\n let checked_exp = something;\n let checked_expr = something_else;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "many_single_char_names", + "id_span": { + "path": "clippy_lints/src/non_expressive_names.rs", + "line": 45 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for too many variables whose name consists of a single character.\n\n **Why is this bad?** It's hard to memorize what a variable means without a\n descriptive name.\n\n **Known problems:** None?\n\n **Example:**\n ```ignore\n let (a, b, c, d, e, f, g) = (...);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "just_underscores_and_digits", + "id_span": { + "path": "clippy_lints/src/non_expressive_names.rs", + "line": 65 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks if you have variables whose name consists of just underscores and digits.\n\n **Why is this bad?** It's hard to memorize what a variable means without a\n descriptive name.\n\n **Known problems:** None?\n\n **Example:**\n ```rust\n let _1 = 1;\n let ___1 = 1;\n let __1___2 = 11;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "nonsensical_open_options", + "id_span": { + "path": "clippy_lints/src/open_options.rs", + "line": 23 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for duplicate open options as well as combinations that make no sense.\n\n **Why is this bad?** In the best case, the code will be harder to read than\n necessary. I don't know the worst case.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n use std::fs::OpenOptions;\n\n OpenOptions::new().read(true).truncate(true);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "option_env_unwrap", + "id_span": { + "path": "clippy_lints/src/option_env_unwrap.rs", + "line": 28 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for usage of `option_env!(...).unwrap()` and suggests usage of the `env!` macro.\n\n **Why is this bad?** Unwrapping the result of `option_env!` will panic\n at run-time if the environment variable doesn't exist, whereas `env!`\n catches it at compile-time.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,no_run\n let _ = option_env!(\"HOME\").unwrap();\n ```\n\n Is better expressed as:\n\n ```rust,no_run\n let _ = env!(\"HOME\");\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "option_if_let_else", + "id_span": { + "path": "clippy_lints/src/option_if_let_else.rs", + "line": 59 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Lints usage of `if let Some(v) = ... { y } else { x }` which is more\n idiomatically done with `Option::map_or` (if the else bit is a pure\n expression) or `Option::map_or_else` (if the else bit is an impure\n expression).\n\n **Why is this bad?**\n Using the dedicated functions of the Option type is clearer and\n more concise than an `if let` expression.\n\n **Known problems:**\n This lint uses a deliberately conservative metric for checking\n if the inside of either body contains breaks or continues which will\n cause it to not suggest a fix if either block contains a loop with\n continues or breaks contained within the loop.\n\n **Example:**\n\n ```rust\n # let optional: Option = Some(0);\n # fn do_complicated_function() -> u32 { 5 };\n let _ = if let Some(foo) = optional {\n foo\n } else {\n 5\n };\n let _ = if let Some(foo) = optional {\n foo\n } else {\n let y = do_complicated_function();\n y*y\n };\n ```\n\n should be\n\n ```rust\n # let optional: Option = Some(0);\n # fn do_complicated_function() -> u32 { 5 };\n let _ = optional.map_or(5, |foo| foo);\n let _ = optional.map_or_else(||{\n let y = do_complicated_function();\n y*y\n }, |foo| foo);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "overflow_check_conditional", + "id_span": { + "path": "clippy_lints/src/overflow_check_conditional.rs", + "line": 21 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Detects classic underflow/overflow checks.\n **Why is this bad?** Most classic C underflow/overflow checks will fail in\n Rust. Users can use functions like `overflowing_*` and `wrapping_*` instead.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let a = 1;\n # let b = 2;\n a + b < a;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "panic_in_result_fn", + "id_span": { + "path": "clippy_lints/src/panic_in_result_fn.rs", + "line": 29 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!`, `unreachable!` or assertions in a function of type result.\n **Why is this bad?** For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence panicking macros should be avoided.\n\n **Known problems:** Functions called from a function returning a `Result` may invoke a panicking macro. This is not checked.\n\n **Example:**\n\n ```rust\n fn result_with_panic() -> Result\n {\n panic!(\"error\");\n }\n ```\n Use instead:\n ```rust\n fn result_without_panic() -> Result {\n Err(String::from(\"error\"))\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "panic", + "id_span": { + "path": "clippy_lints/src/panic_unimplemented.rs", + "line": 19 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for usage of `panic!`.\n **Why is this bad?** `panic!` will stop the execution of the executable\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n panic!(\"even with a good reason\");\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "unimplemented", + "id_span": { + "path": "clippy_lints/src/panic_unimplemented.rs", + "line": 35 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for usage of `unimplemented!`.\n **Why is this bad?** This macro should not be present in production code\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n unimplemented!();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "todo", + "id_span": { + "path": "clippy_lints/src/panic_unimplemented.rs", + "line": 51 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for usage of `todo!`.\n **Why is this bad?** This macro should not be present in production code\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n todo!();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "unreachable", + "id_span": { + "path": "clippy_lints/src/panic_unimplemented.rs", + "line": 67 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for usage of `unreachable!`.\n **Why is this bad?** This macro can cause code to panic\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n unreachable!();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "partialeq_ne_impl", + "id_span": { + "path": "clippy_lints/src/partialeq_ne_impl.rs", + "line": 27 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for manual re-implementations of `PartialEq::ne`.\n **Why is this bad?** `PartialEq::ne` is required to always return the\n negated result of `PartialEq::eq`, which is exactly what the default\n implementation does. Therefore, there should never be any need to\n re-implement it.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n struct Foo;\n\n impl PartialEq for Foo {\n fn eq(&self, other: &Foo) -> bool { true }\n fn ne(&self, other: &Foo) -> bool { !(self == other) }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "trivially_copy_pass_by_ref", + "id_span": { + "path": "clippy_lints/src/pass_by_ref_or_value.rs", + "line": 52 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for functions taking arguments by reference, where the argument type is `Copy` and small enough to be more efficient to always\n pass by value.\n\n **Why is this bad?** In many calling conventions instances of structs will\n be passed through registers if they fit into two or less general purpose\n registers.\n\n **Known problems:** This lint is target register size dependent, it is\n limited to 32-bit to try and reduce portability problems between 32 and\n 64-bit, but if you are compiling for 8 or 16-bit targets then the limit\n will be different.\n\n The configuration option `trivial_copy_size_limit` can be set to override\n this limit for a project.\n\n This lint attempts to allow passing arguments by reference if a reference\n to that argument is returned. This is implemented by comparing the lifetime\n of the argument and return value for equality. However, this can cause\n false positives in cases involving multiple lifetimes that are bounded by\n each other.\n\n **Example:**\n\n ```rust\n // Bad\n fn foo(v: &u32) {}\n ```\n\n ```rust\n // Better\n fn foo(v: u32) {}\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "large_types_passed_by_value", + "id_span": { + "path": "clippy_lints/src/pass_by_ref_or_value.rs", + "line": 84 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for functions taking arguments by value, where the argument type is `Copy` and large enough to be worth considering\n passing by reference. Does not trigger if the function is being exported,\n because that might induce API breakage, if the parameter is declared as mutable,\n or if the argument is a `self`.\n\n **Why is this bad?** Arguments passed by value might result in an unnecessary\n shallow copy, taking up more space in the stack and requiring a call to\n `memcpy`, which can be expensive.\n\n **Example:**\n\n ```rust\n #[derive(Clone, Copy)]\n struct TooLarge([u8; 2048]);\n\n // Bad\n fn foo(v: TooLarge) {}\n ```\n ```rust\n #[derive(Clone, Copy)]\n struct TooLarge([u8; 2048]);\n\n // Good\n fn foo(v: &TooLarge) {}\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "path_buf_push_overwrite", + "id_span": { + "path": "clippy_lints/src/path_buf_push_overwrite.rs", + "line": 36 + }, + "group": "clippy::nursery", + "docs": " **What it does:*** Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push) calls on `PathBuf` that can cause overwrites.\n\n **Why is this bad?** Calling `push` with a root path at the start can overwrite the\n previous defined path.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n use std::path::PathBuf;\n\n let mut x = PathBuf::from(\"/foo\");\n x.push(\"/bar\");\n assert_eq!(x, PathBuf::from(\"/bar\"));\n ```\n Could be written:\n\n ```rust\n use std::path::PathBuf;\n\n let mut x = PathBuf::from(\"/foo\");\n x.push(\"bar\");\n assert_eq!(x, PathBuf::from(\"/foo/bar\"));\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "pattern_type_mismatch", + "id_span": { + "path": "clippy_lints/src/pattern_type_mismatch.rs", + "line": 79 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for patterns that aren't exact representations of the types they are applied to.\n\n To satisfy this lint, you will have to adjust either the expression that is matched\n against or the pattern itself, as well as the bindings that are introduced by the\n adjusted patterns. For matching you will have to either dereference the expression\n with the `*` operator, or amend the patterns to explicitly match against `&`\n or `&mut ` depending on the reference mutability. For the bindings you need\n to use the inverse. You can leave them as plain bindings if you wish for the value\n to be copied, but you must use `ref mut ` or `ref ` to construct\n a reference into the matched structure.\n\n If you are looking for a way to learn about ownership semantics in more detail, it\n is recommended to look at IDE options available to you to highlight types, lifetimes\n and reference semantics in your code. The available tooling would expose these things\n in a general way even outside of the various pattern matching mechanics. Of course\n this lint can still be used to highlight areas of interest and ensure a good understanding\n of ownership semantics.\n\n **Why is this bad?** It isn't bad in general. But in some contexts it can be desirable\n because it increases ownership hints in the code, and will guard against some changes\n in ownership.\n\n **Known problems:** None.\n\n **Example:**\n\n This example shows the basic adjustments necessary to satisfy the lint. Note how\n the matched expression is explicitly dereferenced with `*` and the `inner` variable\n is bound to a shared borrow via `ref inner`.\n\n ```rust,ignore\n // Bad\n let value = &Some(Box::new(23));\n match value {\n Some(inner) => println!(\"{}\", inner),\n None => println!(\"none\"),\n }\n\n // Good\n let value = &Some(Box::new(23));\n match *value {\n Some(ref inner) => println!(\"{}\", inner),\n None => println!(\"none\"),\n }\n ```\n\n The following example demonstrates one of the advantages of the more verbose style.\n Note how the second version uses `ref mut a` to explicitly declare `a` a shared mutable\n borrow, while `b` is simply taken by value. This ensures that the loop body cannot\n accidentally modify the wrong part of the structure.\n\n ```rust,ignore\n // Bad\n let mut values = vec![(2, 3), (3, 4)];\n for (a, b) in &mut values {\n *a += *b;\n }\n\n // Good\n let mut values = vec![(2, 3), (3, 4)];\n for &mut (ref mut a, b) in &mut values {\n *a += b;\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "precedence", + "id_span": { + "path": "clippy_lints/src/precedence.rs", + "line": 44 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for operations where precedence may be unclear and suggests to add parentheses. Currently it catches the following:\n * mixed usage of arithmetic and bit shifting/combining operators without\n parentheses\n * a \"negative\" numeric literal (which is really a unary `-` followed by a\n numeric literal)\n followed by a method call\n\n **Why is this bad?** Not everyone knows the precedence of those operators by\n heart, so expressions like these may trip others trying to reason about the\n code.\n\n **Known problems:** None.\n\n **Example:**\n * `1 << 2 + 3` equals 32, while `(1 << 2) + 3` equals 7\n * `-1i32.abs()` equals -1, while `(-1i32).abs()` equals 1\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "ptr_arg", + "id_span": { + "path": "clippy_lints/src/ptr.rs", + "line": 69 + }, + "group": "clippy::style", + "docs": " **What it does:** This lint checks for function arguments of type `&String` or `&Vec` unless the references are mutable. It will also suggest you\n replace `.clone()` calls with the appropriate `.to_owned()`/`to_string()`\n calls.\n\n **Why is this bad?** Requiring the argument to be of the specific size\n makes the function less useful for no benefit; slices in the form of `&[T]`\n or `&str` usually suffice and can be obtained from other types, too.\n\n **Known problems:** The lint does not follow data. So if you have an\n argument `x` and write `let y = x; y.clone()` the lint will not suggest\n changing that `.clone()` to `.to_owned()`.\n\n Other functions called from this function taking a `&String` or `&Vec`\n argument may also fail to compile if you change the argument. Applying\n this lint on them will fix the problem, but they may be in other crates.\n\n One notable example of a function that may cause issues, and which cannot\n easily be changed due to being in the standard library is `Vec::contains`.\n when called on a `Vec>`. If a `&Vec` is passed to that method then\n it will compile, but if a `&[T]` is passed then it will not compile.\n\n ```ignore\n fn cannot_take_a_slice(v: &Vec) -> bool {\n let vec_of_vecs: Vec> = some_other_fn();\n\n vec_of_vecs.contains(v)\n }\n ```\n\n Also there may be `fn(&Vec)`-typed references pointing to your function.\n If you have them, you will get a compiler error after applying this lint's\n suggestions. You then have the choice to undo your changes or change the\n type of the reference.\n\n Note that if the function is part of your public interface, there may be\n other crates referencing it, of which you may not be aware. Carefully\n deprecate the function before applying the lint suggestions in this case.\n\n **Example:**\n ```ignore\n // Bad\n fn foo(&Vec) { .. }\n\n // Good\n fn foo(&[u32]) { .. }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "cmp_null", + "id_span": { + "path": "clippy_lints/src/ptr.rs", + "line": 95 + }, + "group": "clippy::style", + "docs": " **What it does:** This lint checks for equality comparisons with `ptr::null`\n **Why is this bad?** It's easier and more readable to use the inherent\n `.is_null()`\n method instead\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n // Bad\n if x == ptr::null {\n ..\n }\n\n // Good\n if x.is_null() {\n ..\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "mut_from_ref", + "id_span": { + "path": "clippy_lints/src/ptr.rs", + "line": 117 + }, + "group": "clippy::correctness", + "docs": " **What it does:** This lint checks for functions that take immutable references and return mutable ones.\n\n **Why is this bad?** This is trivially unsound, as one can create two\n mutable references from the same (immutable!) source.\n This [error](https://github.com/rust-lang/rust/issues/39465)\n actually lead to an interim Rust release 1.15.1.\n\n **Known problems:** To be on the conservative side, if there's at least one\n mutable reference with the output lifetime, this lint will not trigger.\n In practice, this case is unlikely anyway.\n\n **Example:**\n ```ignore\n fn foo(&Foo) -> &mut Bar { .. }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "ptr_eq", + "id_span": { + "path": "clippy_lints/src/ptr_eq.rs", + "line": 32 + }, + "group": "clippy::style", + "docs": " **What it does:** Use `std::ptr::eq` when applicable\n **Why is this bad?** `ptr::eq` can be used to compare `&T` references\n (which coerce to `*const T` implicitly) by their address rather than\n comparing the values they point to.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let a = &[1, 2, 3];\n let b = &[1, 2, 3];\n\n assert!(a as *const _ as usize == b as *const _ as usize);\n ```\n Use instead:\n ```rust\n let a = &[1, 2, 3];\n let b = &[1, 2, 3];\n\n assert!(std::ptr::eq(a, b));\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "ptr_offset_with_cast", + "id_span": { + "path": "clippy_lints/src/ptr_offset_with_cast.rs", + "line": 40 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of the `offset` pointer method with a `usize` casted to an `isize`.\n\n **Why is this bad?** If we’re always increasing the pointer address, we can avoid the numeric\n cast by using the `add` method instead.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n let vec = vec![b'a', b'b', b'c'];\n let ptr = vec.as_ptr();\n let offset = 1_usize;\n\n unsafe {\n ptr.offset(offset as isize);\n }\n ```\n\n Could be written:\n\n ```rust\n let vec = vec![b'a', b'b', b'c'];\n let ptr = vec.as_ptr();\n let offset = 1_usize;\n\n unsafe {\n ptr.add(offset);\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "question_mark", + "id_span": { + "path": "clippy_lints/src/question_mark.rs", + "line": 34 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for expressions that could be replaced by the question mark operator.\n **Why is this bad?** Question mark usage is more idiomatic.\n\n **Known problems:** None\n\n **Example:**\n ```ignore\n if option.is_none() {\n return None;\n }\n ```\n\n Could be written:\n\n ```ignore\n option?;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "range_zip_with_len", + "id_span": { + "path": "clippy_lints/src/ranges.rs", + "line": 40 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for zipping a collection with the range of `0.._.len()`.\n\n **Why is this bad?** The code is better expressed with `.enumerate()`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = vec![1];\n x.iter().zip(0..x.len());\n ```\n Could be written as\n ```rust\n # let x = vec![1];\n x.iter().enumerate();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "range_plus_one", + "id_span": { + "path": "clippy_lints/src/ranges.rs", + "line": 74 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for exclusive ranges where 1 is added to the upper bound, e.g., `x..(y+1)`.\n\n **Why is this bad?** The code is more readable with an inclusive range\n like `x..=y`.\n\n **Known problems:** Will add unnecessary pair of parentheses when the\n expression is not wrapped in a pair but starts with a opening parenthesis\n and ends with a closing one.\n I.e., `let _ = (f()+1)..(f()+1)` results in `let _ = ((f()+1)..=f())`.\n\n Also in many cases, inclusive ranges are still slower to run than\n exclusive ranges, because they essentially add an extra branch that\n LLVM may fail to hoist out of the loop.\n\n This will cause a warning that cannot be fixed if the consumer of the\n range only accepts a specific range type, instead of the generic\n `RangeBounds` trait\n ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).\n\n **Example:**\n ```rust,ignore\n for x..(y+1) { .. }\n ```\n Could be written as\n ```rust,ignore\n for x..=y { .. }\n ```\n", + "applicability": { + "is_multi_suggestion": true, + "applicability": "MachineApplicable" + } + }, + { + "id": "range_minus_one", + "id_span": { + "path": "clippy_lints/src/ranges.rs", + "line": 99 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for inclusive ranges where 1 is subtracted from the upper bound, e.g., `x..=(y-1)`.\n\n **Why is this bad?** The code is more readable with an exclusive range\n like `x..y`.\n\n **Known problems:** This will cause a warning that cannot be fixed if\n the consumer of the range only accepts a specific range type, instead of\n the generic `RangeBounds` trait\n ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).\n\n **Example:**\n ```rust,ignore\n for x..=(y-1) { .. }\n ```\n Could be written as\n ```rust,ignore\n for x..y { .. }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "reversed_empty_ranges", + "id_span": { + "path": "clippy_lints/src/ranges.rs", + "line": 132 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for range expressions `x..y` where both `x` and `y` are constant and `x` is greater or equal to `y`.\n\n **Why is this bad?** Empty ranges yield no values so iterating them is a no-op.\n Moreover, trying to use a reversed range to index a slice will panic at run-time.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,no_run\n fn main() {\n (10..=0).for_each(|x| println!(\"{}\", x));\n\n let arr = [1, 2, 3, 4, 5];\n let sub = &arr[3..1];\n }\n ```\n Use instead:\n ```rust\n fn main() {\n (0..=10).rev().for_each(|x| println!(\"{}\", x));\n\n let arr = [1, 2, 3, 4, 5];\n let sub = &arr[1..3];\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "manual_range_contains", + "id_span": { + "path": "clippy_lints/src/ranges.rs", + "line": 159 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for expressions like `x >= 3 && x < 8` that could be more readably expressed as `(3..8).contains(x)`.\n\n **Why is this bad?** `contains` expresses the intent better and has less\n failure modes (such as fencepost errors or using `||` instead of `&&`).\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // given\n let x = 6;\n\n assert!(x >= 3 && x < 8);\n ```\n Use instead:\n ```rust\n# let x = 6;\n assert!((3..8).contains(&x));\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "redundant_clone", + "id_span": { + "path": "clippy_lints/src/redundant_clone.rs", + "line": 63 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for a redundant `clone()` (and its relatives) which clones an owned value that is going to be dropped without further use.\n\n **Why is this bad?** It is not always possible for the compiler to eliminate useless\n allocations and deallocations generated by redundant `clone()`s.\n\n **Known problems:**\n\n False-negatives: analysis performed by this lint is conservative and limited.\n\n **Example:**\n ```rust\n # use std::path::Path;\n # #[derive(Clone)]\n # struct Foo;\n # impl Foo {\n # fn new() -> Self { Foo {} }\n # }\n # fn call(x: Foo) {}\n {\n let x = Foo::new();\n call(x.clone());\n call(x.clone()); // this can just pass `x`\n }\n\n [\"lorem\", \"ipsum\"].join(\" \").to_string();\n\n Path::new(\"/a/b\").join(\"c\").to_path_buf();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "redundant_closure_call", + "id_span": { + "path": "clippy_lints/src/redundant_closure_call.rs", + "line": 32 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Detects closures called in the same expression where they are defined.\n\n **Why is this bad?** It is unnecessarily adding to the expression's\n complexity.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n // Bad\n let a = (|| 42)()\n\n // Good\n let a = 42\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "redundant_else", + "id_span": { + "path": "clippy_lints/src/redundant_else.rs", + "line": 37 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for `else` blocks that can be removed without changing semantics.\n **Why is this bad?** The `else` block adds unnecessary indentation and verbosity.\n\n **Known problems:** Some may prefer to keep the `else` block for clarity.\n\n **Example:**\n\n ```rust\n fn my_func(count: u32) {\n if count == 0 {\n print!(\"Nothing to do\");\n return;\n } else {\n print!(\"Moving on...\");\n }\n }\n ```\n Use instead:\n ```rust\n fn my_func(count: u32) {\n if count == 0 {\n print!(\"Nothing to do\");\n return;\n }\n print!(\"Moving on...\");\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "redundant_field_names", + "id_span": { + "path": "clippy_lints/src/redundant_field_names.rs", + "line": 34 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for fields in struct literals where shorthands could be used.\n\n **Why is this bad?** If the field and variable names are the same,\n the field name is redundant.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let bar: u8 = 123;\n\n struct Foo {\n bar: u8,\n }\n\n let foo = Foo { bar: bar };\n ```\n the last line can be simplified to\n ```ignore\n let foo = Foo { bar };\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "redundant_pub_crate", + "id_span": { + "path": "clippy_lints/src/redundant_pub_crate.rs", + "line": 30 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Checks for items declared `pub(crate)` that are not crate visible because they are inside a private module.\n\n **Why is this bad?** Writing `pub(crate)` is misleading when it's redundant due to the parent\n module's visibility.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n mod internal {\n pub(crate) fn internal_fn() { }\n }\n ```\n This function is not visible outside the module and it can be declared with `pub` or\n private visibility\n ```rust\n mod internal {\n pub fn internal_fn() { }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "redundant_slicing", + "id_span": { + "path": "clippy_lints/src/redundant_slicing.rs", + "line": 33 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for redundant slicing expressions which use the full range, and do not change the type.\n\n **Why is this bad?** It unnecessarily adds complexity to the expression.\n\n **Known problems:** If the type being sliced has an implementation of `Index`\n that actually changes anything then it can't be removed. However, this would be surprising\n to people reading the code and should have a note with it.\n\n **Example:**\n\n ```ignore\n fn get_slice(x: &[u32]) -> &[u32] {\n &x[..]\n }\n ```\n Use instead:\n ```ignore\n fn get_slice(x: &[u32]) -> &[u32] {\n x\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "redundant_static_lifetimes", + "id_span": { + "path": "clippy_lints/src/redundant_static_lifetimes.rs", + "line": 30 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for constants and statics with an explicit `'static` lifetime.\n **Why is this bad?** Adding `'static` to every reference can create very\n complicated types.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n const FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] =\n &[...]\n static FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] =\n &[...]\n ```\n This code can be rewritten as\n ```ignore\n const FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]\n static FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "ref_option_ref", + "id_span": { + "path": "clippy_lints/src/ref_option_ref.rs", + "line": 28 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for usage of `&Option<&T>`.\n **Why is this bad?** Since `&` is Copy, it's useless to have a\n reference on `Option<&T>`.\n\n **Known problems:** It may be irrelevant to use this lint on\n public API code as it will make a breaking change to apply it.\n\n **Example:**\n\n ```rust,ignore\n let x: &Option<&u32> = &Some(&0u32);\n ```\n Use instead:\n ```rust,ignore\n let x: Option<&u32> = Some(&0u32);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "deref_addrof", + "id_span": { + "path": "clippy_lints/src/reference.rs", + "line": 29 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `*&` and `*&mut` in expressions.\n **Why is this bad?** Immediately dereferencing a reference is no-op and\n makes the code less clear.\n\n **Known problems:** Multiple dereference/addrof pairs are not handled so\n the suggested fix for `x = **&&y` is `x = *&y`, which is still incorrect.\n\n **Example:**\n ```rust,ignore\n // Bad\n let a = f(*&mut b);\n let c = *&d;\n\n // Good\n let a = f(b);\n let c = d;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "ref_in_deref", + "id_span": { + "path": "clippy_lints/src/reference.rs", + "line": 120 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for references in expressions that use auto dereference.\n\n **Why is this bad?** The reference is a no-op and is automatically\n dereferenced by the compiler and makes the code less clear.\n\n **Example:**\n ```rust\n struct Point(u32, u32);\n let point = Point(30, 20);\n let x = (&point).0;\n ```\n Use instead:\n ```rust\n # struct Point(u32, u32);\n # let point = Point(30, 20);\n let x = point.0;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "invalid_regex", + "id_span": { + "path": "clippy_lints/src/regex.rs", + "line": 25 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks [regex](https://crates.io/crates/regex) creation (with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`) for correct\n regex syntax.\n\n **Why is this bad?** This will lead to a runtime panic.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n Regex::new(\"|\")\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "trivial_regex", + "id_span": { + "path": "clippy_lints/src/regex.rs", + "line": 46 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Checks for trivial [regex](https://crates.io/crates/regex) creation (with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`).\n\n **Why is this bad?** Matching the regex can likely be replaced by `==` or\n `str::starts_with`, `str::ends_with` or `std::contains` or other `str`\n methods.\n\n **Known problems:** If the same regex is going to be applied to multiple\n inputs, the precomputations done by `Regex` construction can give\n significantly better performance than any of the `str`-based methods.\n\n **Example:**\n ```ignore\n Regex::new(\"^foobar\")\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "repeat_once", + "id_span": { + "path": "clippy_lints/src/repeat_once.rs", + "line": 33 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for usage of `.repeat(1)` and suggest the following method for each types. - `.to_string()` for `str`\n - `.clone()` for `String`\n - `.to_vec()` for `slice`\n\n **Why is this bad?** For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning the string is the intention behind this, `clone()` should be used.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n fn main() {\n let x = String::from(\"hello world\").repeat(1);\n }\n ```\n Use instead:\n ```rust\n fn main() {\n let x = String::from(\"hello world\").clone();\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "let_and_return", + "id_span": { + "path": "clippy_lints/src/returns.rs", + "line": 38 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `let`-bindings, which are subsequently returned.\n\n **Why is this bad?** It is just extraneous code. Remove it to make your code\n more rusty.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn foo() -> String {\n let x = String::new();\n x\n }\n ```\n instead, use\n ```\n fn foo() -> String {\n String::new()\n }\n ```\n", + "applicability": { + "is_multi_suggestion": true, + "applicability": "MachineApplicable" + } + }, + { + "id": "needless_return", + "id_span": { + "path": "clippy_lints/src/returns.rs", + "line": 63 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for return statements at the end of a block.\n **Why is this bad?** Removing the `return` and semicolon will make the code\n more rusty.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn foo(x: usize) -> usize {\n return x;\n }\n ```\n simplify to\n ```rust\n fn foo(x: usize) -> usize {\n x\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "self_assignment", + "id_span": { + "path": "clippy_lints/src/self_assignment.rs", + "line": 29 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for explicit self-assignments.\n **Why is this bad?** Self-assignments are redundant and unlikely to be\n intentional.\n\n **Known problems:** If expression contains any deref coercions or\n indexing operations they are assumed not to have any side effects.\n\n **Example:**\n\n ```rust\n struct Event {\n id: usize,\n x: i32,\n y: i32,\n }\n\n fn copy_position(a: &mut Event, b: &Event) {\n a.x = b.x;\n a.y = a.y;\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "semicolon_if_nothing_returned", + "id_span": { + "path": "clippy_lints/src/semicolon_if_nothing_returned.rs", + "line": 30 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Looks for blocks of expressions and fires if the last expression returns `()` but is not followed by a semicolon.\n\n **Why is this bad?** The semicolon might be optional but when\n extending the block with new code, it doesn't require a change in previous last line.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n fn main() {\n println!(\"Hello world\")\n }\n ```\n Use instead:\n ```rust\n fn main() {\n println!(\"Hello world\");\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "serde_api_misuse", + "id_span": { + "path": "clippy_lints/src/serde_api.rs", + "line": 16 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for mis-uses of the serde API.\n **Why is this bad?** Serde is very finnicky about how its API should be\n used, but the type system can't be used to enforce it (yet?).\n\n **Known problems:** None.\n\n **Example:** Implementing `Visitor::visit_string` but not\n `Visitor::visit_str`.\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "shadow_same", + "id_span": { + "path": "clippy_lints/src/shadow.rs", + "line": 34 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for bindings that shadow other bindings already in scope, while just changing reference level or mutability.\n\n **Why is this bad?** Not much, in fact it's a very common pattern in Rust\n code. Still, some may opt to avoid it in their code base, they can set this\n lint to `Warn`.\n\n **Known problems:** This lint, as the other shadowing related lints,\n currently only catches very simple patterns.\n\n **Example:**\n ```rust\n # let x = 1;\n // Bad\n let x = &x;\n\n // Good\n let y = &x; // use different variable name\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "shadow_reuse", + "id_span": { + "path": "clippy_lints/src/shadow.rs", + "line": 61 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for bindings that shadow other bindings already in scope, while reusing the original value.\n\n **Why is this bad?** Not too much, in fact it's a common pattern in Rust\n code. Still, some argue that name shadowing like this hurts readability,\n because a value may be bound to different things depending on position in\n the code.\n\n **Known problems:** This lint, as the other shadowing related lints,\n currently only catches very simple patterns.\n\n **Example:**\n ```rust\n let x = 2;\n let x = x + 1;\n ```\n use different variable name:\n ```rust\n let x = 2;\n let y = x + 1;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "shadow_unrelated", + "id_span": { + "path": "clippy_lints/src/shadow.rs", + "line": 93 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for bindings that shadow other bindings already in scope, either without a initialization or with one that does not even use\n the original value.\n\n **Why is this bad?** Name shadowing can hurt readability, especially in\n large code bases, because it is easy to lose track of the active binding at\n any place in the code. This can be alleviated by either giving more specific\n names to bindings or introducing more scopes to contain the bindings.\n\n **Known problems:** This lint, as the other shadowing related lints,\n currently only catches very simple patterns. Note that\n `allow`/`warn`/`deny`/`forbid` attributes only work on the function level\n for this lint.\n\n **Example:**\n ```rust\n # let y = 1;\n # let z = 2;\n let x = y;\n\n // Bad\n let x = z; // shadows the earlier binding\n\n // Good\n let w = z; // use different variable name\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "single_component_path_imports", + "id_span": { + "path": "clippy_lints/src/single_component_path_imports.rs", + "line": 32 + }, + "group": "clippy::style", + "docs": " **What it does:** Checking for imports with single component use path.\n **Why is this bad?** Import with single component use path such as `use cratename;`\n is not necessary, and thus should be removed.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,ignore\n use regex;\n\n fn main() {\n regex::Regex::new(r\"^\\d{4}-\\d{2}-\\d{2}$\").unwrap();\n }\n ```\n Better as\n ```rust,ignore\n fn main() {\n regex::Regex::new(r\"^\\d{4}-\\d{2}-\\d{2}$\").unwrap();\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "size_of_in_element_count", + "id_span": { + "path": "clippy_lints/src/size_of_in_element_count.rs", + "line": 31 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Detects expressions where `size_of::` or `size_of_val::` is used as a\n count of elements of type `T`\n\n **Why is this bad?** These functions expect a count\n of `T` and not a number of bytes\n\n **Known problems:** None.\n\n **Example:**\n ```rust,no_run\n # use std::ptr::copy_nonoverlapping;\n # use std::mem::size_of;\n const SIZE: usize = 128;\n let x = [2u8; SIZE];\n let mut y = [2u8; SIZE];\n unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) };\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "slow_vector_initialization", + "id_span": { + "path": "clippy_lints/src/slow_vector_initialization.rs", + "line": 37 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks slow zero-filled vector initialization\n **Why is this bad?** These structures are non-idiomatic and less efficient than simply using\n `vec![0; len]`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use core::iter::repeat;\n # let len = 4;\n\n // Bad\n let mut vec1 = Vec::with_capacity(len);\n vec1.resize(len, 0);\n\n let mut vec2 = Vec::with_capacity(len);\n vec2.extend(repeat(0).take(len));\n\n // Good\n let mut vec1 = vec![0; len];\n let mut vec2 = vec![0; len];\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "stable_sort_primitive", + "id_span": { + "path": "clippy_lints/src/stable_sort_primitive.rs", + "line": 36 + }, + "group": "clippy::perf", + "docs": " **What it does:** When sorting primitive values (integers, bools, chars, as well\n as arrays, slices, and tuples of such items), it is better to\n use an unstable sort than a stable sort.\n\n **Why is this bad?**\n Using a stable sort consumes more memory and cpu cycles. Because\n values which compare equal are identical, preserving their\n relative order (the guarantee that a stable sort provides) means\n nothing, while the extra costs still apply.\n\n **Known problems:**\n None\n\n **Example:**\n\n ```rust\n let mut vec = vec![2, 1, 3];\n vec.sort();\n ```\n Use instead:\n ```rust\n let mut vec = vec![2, 1, 3];\n vec.sort_unstable();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "string_add_assign", + "id_span": { + "path": "clippy_lints/src/strings.rs", + "line": 37 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for string appends of the form `x = x + y` (without `let`!).\n\n **Why is this bad?** It's not really bad, but some people think that the\n `.push_str(_)` method is more readable.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let mut x = \"Hello\".to_owned();\n x = x + \", World\";\n\n // More readable\n x += \", World\";\n x.push_str(\", World\");\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "string_add", + "id_span": { + "path": "clippy_lints/src/strings.rs", + "line": 65 + }, + "group": "clippy::restriction", + "docs": " **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*\n match.\n\n **Why is this bad?** It's not bad in and of itself. However, this particular\n `Add` implementation is asymmetric (the other operand need not be `String`,\n but `x` does), while addition as mathematically defined is symmetric, also\n the `String::push_str(_)` function is a perfectly good replacement.\n Therefore, some dislike it and wish not to have it in their code.\n\n That said, other people think that string addition, having a long tradition\n in other languages is actually fine, which is why we decided to make this\n particular lint `allow` by default.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let x = \"Hello\".to_owned();\n x + \", World\";\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "string_lit_as_bytes", + "id_span": { + "path": "clippy_lints/src/strings.rs", + "line": 107 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Checks for the `as_bytes` method called on string literals that contain only ASCII characters.\n\n **Why is this bad?** Byte string literals (e.g., `b\"foo\"`) can be used\n instead. They are shorter but less discoverable than `as_bytes()`.\n\n **Known Problems:**\n `\"str\".as_bytes()` and the suggested replacement of `b\"str\"` are not\n equivalent because they have different types. The former is `&[u8]`\n while the latter is `&[u8; 3]`. That means in general they will have a\n different set of methods and different trait implementations.\n\n ```compile_fail\n fn f(v: Vec) {}\n\n f(\"...\".as_bytes().to_owned()); // works\n f(b\"...\".to_owned()); // does not work, because arg is [u8; 3] not Vec\n\n fn g(r: impl std::io::Read) {}\n\n g(\"...\".as_bytes()); // works\n g(b\"...\"); // does not work\n ```\n\n The actual equivalent of `\"str\".as_bytes()` with the same type is not\n `b\"str\"` but `&b\"str\"[..]`, which is a great deal of punctuation and not\n more readable than a function call.\n\n **Example:**\n ```rust\n // Bad\n let bs = \"a byte string\".as_bytes();\n\n // Good\n let bs = b\"a byte string\";\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "string_from_utf8_as_bytes", + "id_span": { + "path": "clippy_lints/src/strings.rs", + "line": 196 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Check if the string is transformed to byte array and casted back to string.\n **Why is this bad?** It's unnecessary, the string can be used directly.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n let _ = std::str::from_utf8(&\"Hello World!\".as_bytes()[6..11]).unwrap();\n ```\n could be written as\n ```rust\n let _ = &\"Hello World!\"[6..11];\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "str_to_string", + "id_span": { + "path": "clippy_lints/src/strings.rs", + "line": 313 + }, + "group": "clippy::restriction", + "docs": " **What it does:** This lint checks for `.to_string()` method calls on values of type `&str`.\n **Why is this bad?** The `to_string` method is also used on other types to convert them to a string.\n When called on a `&str` it turns the `&str` into the owned variant `String`, which can be better\n expressed with `.to_owned()`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // example code where clippy issues a warning\n let _ = \"str\".to_string();\n ```\n Use instead:\n ```rust\n // example code which does not raise clippy warning\n let _ = \"str\".to_owned();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "string_to_string", + "id_span": { + "path": "clippy_lints/src/strings.rs", + "line": 362 + }, + "group": "clippy::restriction", + "docs": " **What it does:** This lint checks for `.to_string()` method calls on values of type `String`.\n **Why is this bad?** The `to_string` method is also used on other types to convert them to a string.\n When called on a `String` it only clones the `String`, which can be better expressed with `.clone()`.\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // example code where clippy issues a warning\n let msg = String::from(\"Hello World\");\n let _ = msg.to_string();\n ```\n Use instead:\n ```rust\n // example code which does not raise clippy warning\n let msg = String::from(\"Hello World\");\n let _ = msg.clone();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "suspicious_operation_groupings", + "id_span": { + "path": "clippy_lints/src/suspicious_operation_groupings.rs", + "line": 60 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for unlikely usages of binary operators that are almost\n certainly typos and/or copy/paste errors, given the other usages\n of binary operators nearby.\n **Why is this bad?**\n They are probably bugs and if they aren't then they look like bugs\n and you should add a comment explaining why you are doing such an\n odd set of operations.\n **Known problems:**\n There may be some false positives if you are trying to do something\n unusual that happens to look like a typo.\n\n **Example:**\n\n ```rust\n struct Vec3 {\n x: f64,\n y: f64,\n z: f64,\n }\n\n impl Eq for Vec3 {}\n\n impl PartialEq for Vec3 {\n fn eq(&self, other: &Self) -> bool {\n // This should trigger the lint because `self.x` is compared to `other.y`\n self.x == other.y && self.y == other.y && self.z == other.z\n }\n }\n ```\n Use instead:\n ```rust\n # struct Vec3 {\n # x: f64,\n # y: f64,\n # z: f64,\n # }\n // same as above except:\n impl PartialEq for Vec3 {\n fn eq(&self, other: &Self) -> bool {\n // Note we now compare other.x to self.x\n self.x == other.x && self.y == other.y && self.z == other.z\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "suspicious_arithmetic_impl", + "id_span": { + "path": "clippy_lints/src/suspicious_trait_impl.rs", + "line": 27 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Lints for suspicious operations in impls of arithmetic operators, e.g. subtracting elements in an Add impl.\n\n **Why this is bad?** This is probably a typo or copy-and-paste error and not intended.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n impl Add for Foo {\n type Output = Foo;\n\n fn add(self, other: Foo) -> Foo {\n Foo(self.0 - other.0)\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "suspicious_op_assign_impl", + "id_span": { + "path": "clippy_lints/src/suspicious_trait_impl.rs", + "line": 48 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Lints for suspicious operations in impls of OpAssign, e.g. subtracting elements in an AddAssign impl.\n\n **Why this is bad?** This is probably a typo or copy-and-paste error and not intended.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n impl AddAssign for Foo {\n fn add_assign(&mut self, other: Foo) {\n *self = *self - other;\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "manual_swap", + "id_span": { + "path": "clippy_lints/src/swap.rs", + "line": 36 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for manual swapping.\n **Why is this bad?** The `std::mem::swap` function exposes the intent better\n without deinitializing or copying either variable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let mut a = 42;\n let mut b = 1337;\n\n let t = b;\n b = a;\n a = t;\n ```\n Use std::mem::swap():\n ```rust\n let mut a = 1;\n let mut b = 2;\n std::mem::swap(&mut a, &mut b);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "almost_swapped", + "id_span": { + "path": "clippy_lints/src/swap.rs", + "line": 61 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for `foo = bar; bar = foo` sequences.\n **Why is this bad?** This looks like a failed attempt to swap.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let mut a = 1;\n # let mut b = 2;\n a = b;\n b = a;\n ```\n If swapping is intended, use `swap()` instead:\n ```rust\n # let mut a = 1;\n # let mut b = 2;\n std::mem::swap(&mut a, &mut b);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "tabs_in_doc_comments", + "id_span": { + "path": "clippy_lints/src/tabs_in_doc_comments.rs", + "line": 54 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks doc comments for usage of tab characters.\n **Why is this bad?** The rust style-guide promotes spaces instead of tabs for indentation.\n To keep a consistent view on the source, also doc comments should not have tabs.\n Also, explaining ascii-diagrams containing tabs can get displayed incorrectly when the\n display settings of the author and reader differ.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n ///\n /// Struct to hold two strings:\n /// \t- first\t\tone\n /// \t- second\tone\n pub struct DoubleString {\n ///\n /// \t- First String:\n /// \t\t- needs to be inside here\n first_string: String,\n ///\n /// \t- Second String:\n /// \t\t- needs to be inside here\n second_string: String,\n}\n ```\n\n Will be converted to:\n ```rust\n ///\n /// Struct to hold two strings:\n /// - first one\n /// - second one\n pub struct DoubleString {\n ///\n /// - First String:\n /// - needs to be inside here\n first_string: String,\n ///\n /// - Second String:\n /// - needs to be inside here\n second_string: String,\n}\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "temporary_assignment", + "id_span": { + "path": "clippy_lints/src/temporary_assignment.rs", + "line": 19 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for construction of a structure or tuple just to assign a value in it.\n\n **Why is this bad?** Readability. If the structure is only created to be\n updated, why not write the structure you want in the first place?\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n (0, 0).0 = 1\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "to_digit_is_some", + "id_span": { + "path": "clippy_lints/src/to_digit_is_some.rs", + "line": 27 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for `.to_digit(..).is_some()` on `char`s.\n **Why is this bad?** This is a convoluted way of checking if a `char` is a digit. It's\n more straight forward to use the dedicated `is_digit` method.\n\n **Example:**\n ```rust\n # let c = 'c';\n # let radix = 10;\n let is_digit = c.to_digit(radix).is_some();\n ```\n can be written as:\n ```\n # let c = 'c';\n # let radix = 10;\n let is_digit = c.is_digit(radix);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "to_string_in_display", + "id_span": { + "path": "clippy_lints/src/to_string_in_display.rs", + "line": 40 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for uses of `to_string()` in `Display` traits.\n **Why is this bad?** Usually `to_string` is implemented indirectly\n via `Display`. Hence using it while implementing `Display` would\n lead to infinite recursion.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n use std::fmt;\n\n struct Structure(i32);\n impl fmt::Display for Structure {\n fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n write!(f, \"{}\", self.to_string())\n }\n }\n\n ```\n Use instead:\n ```rust\n use std::fmt;\n\n struct Structure(i32);\n impl fmt::Display for Structure {\n fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n write!(f, \"{}\", self.0)\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "type_repetition_in_bounds", + "id_span": { + "path": "clippy_lints/src/trait_bounds.rs", + "line": 28 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** This lint warns about unnecessary type repetitions in trait bounds\n **Why is this bad?** Repeating the type for every bound makes the code\n less readable than combining the bounds\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n pub fn foo(t: T) where T: Copy, T: Clone {}\n ```\n\n Could be written as:\n\n ```rust\n pub fn foo(t: T) where T: Copy + Clone {}\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "trait_duplication_in_bounds", + "id_span": { + "path": "clippy_lints/src/trait_bounds.rs", + "line": 57 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for cases where generics are being used and multiple syntax specifications for trait bounds are used simultaneously.\n\n **Why is this bad?** Duplicate bounds makes the code\n less readable than specifing them only once.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn func(arg: T) where T: Clone + Default {}\n ```\n\n Could be written as:\n\n ```rust\n fn func(arg: T) {}\n ```\n or\n\n ```rust\n fn func(arg: T) where T: Clone + Default {}\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "wrong_transmute", + "id_span": { + "path": "clippy_lints/src/transmute.rs", + "line": 29 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for transmutes that can't ever be correct on any architecture.\n\n **Why is this bad?** It's basically guaranteed to be undefined behaviour.\n\n **Known problems:** When accessing C, users might want to store pointer\n sized objects in `extradata` arguments to save an allocation.\n\n **Example:**\n ```ignore\n let ptr: *const T = core::intrinsics::transmute('x')\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "useless_transmute", + "id_span": { + "path": "clippy_lints/src/transmute.rs", + "line": 48 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Checks for transmutes to the original type of the object and transmutes that could be a cast.\n\n **Why is this bad?** Readability. The code tricks people into thinking that\n something complex is going on.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n core::intrinsics::transmute(t); // where the result type is the same as `t`'s\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "transmutes_expressible_as_ptr_casts", + "id_span": { + "path": "clippy_lints/src/transmute.rs", + "line": 73 + }, + "group": "clippy::complexity", + "docs": " **What it does:**Checks for transmutes that could be a pointer cast.\n **Why is this bad?** Readability. The code tricks people into thinking that\n something complex is going on.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # let p: *const [i32] = &[];\n unsafe { std::mem::transmute::<*const [i32], *const [u16]>(p) };\n ```\n Use instead:\n ```rust\n # let p: *const [i32] = &[];\n p as *const [u16];\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "crosspointer_transmute", + "id_span": { + "path": "clippy_lints/src/transmute.rs", + "line": 91 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for transmutes between a type `T` and `*T`.\n **Why is this bad?** It's easy to mistakenly transmute between a type and a\n pointer to that type.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n core::intrinsics::transmute(t) // where the result type is the same as\n // `*t` or `&t`'s\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "transmute_ptr_to_ref", + "id_span": { + "path": "clippy_lints/src/transmute.rs", + "line": 116 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for transmutes from a pointer to a reference.\n **Why is this bad?** This can always be rewritten with `&` and `*`.\n\n **Known problems:**\n - `mem::transmute` in statics and constants is stable from Rust 1.46.0,\n while dereferencing raw pointer is not stable yet.\n If you need to do this in those places,\n you would have to use `transmute` instead.\n\n **Example:**\n ```rust,ignore\n unsafe {\n let _: &T = std::mem::transmute(p); // where p: *const T\n }\n\n // can be written:\n let _: &T = &*p;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "transmute_int_to_char", + "id_span": { + "path": "clippy_lints/src/transmute.rs", + "line": 147 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for transmutes from an integer to a `char`.\n **Why is this bad?** Not every integer is a Unicode scalar value.\n\n **Known problems:**\n - [`from_u32`] which this lint suggests using is slower than `transmute`\n as it needs to validate the input.\n If you are certain that the input is always a valid Unicode scalar value,\n use [`from_u32_unchecked`] which is as fast as `transmute`\n but has a semantically meaningful name.\n - You might want to handle `None` returned from [`from_u32`] instead of calling `unwrap`.\n\n [`from_u32`]: https://doc.rust-lang.org/std/char/fn.from_u32.html\n [`from_u32_unchecked`]: https://doc.rust-lang.org/std/char/fn.from_u32_unchecked.html\n\n **Example:**\n ```rust\n let x = 1_u32;\n unsafe {\n let _: char = std::mem::transmute(x); // where x: u32\n }\n\n // should be:\n let _ = std::char::from_u32(x).unwrap();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "transmute_bytes_to_str", + "id_span": { + "path": "clippy_lints/src/transmute.rs", + "line": 178 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for transmutes from a `&[u8]` to a `&str`.\n **Why is this bad?** Not every byte slice is a valid UTF-8 string.\n\n **Known problems:**\n - [`from_utf8`] which this lint suggests using is slower than `transmute`\n as it needs to validate the input.\n If you are certain that the input is always a valid UTF-8,\n use [`from_utf8_unchecked`] which is as fast as `transmute`\n but has a semantically meaningful name.\n - You might want to handle errors returned from [`from_utf8`] instead of calling `unwrap`.\n\n [`from_utf8`]: https://doc.rust-lang.org/std/str/fn.from_utf8.html\n [`from_utf8_unchecked`]: https://doc.rust-lang.org/std/str/fn.from_utf8_unchecked.html\n\n **Example:**\n ```rust\n let b: &[u8] = &[1_u8, 2_u8];\n unsafe {\n let _: &str = std::mem::transmute(b); // where b: &[u8]\n }\n\n // should be:\n let _ = std::str::from_utf8(b).unwrap();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "transmute_int_to_bool", + "id_span": { + "path": "clippy_lints/src/transmute.rs", + "line": 200 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for transmutes from an integer to a `bool`.\n **Why is this bad?** This might result in an invalid in-memory representation of a `bool`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = 1_u8;\n unsafe {\n let _: bool = std::mem::transmute(x); // where x: u8\n }\n\n // should be:\n let _: bool = x != 0;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "transmute_int_to_float", + "id_span": { + "path": "clippy_lints/src/transmute.rs", + "line": 222 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for transmutes from an integer to a float.\n **Why is this bad?** Transmutes are dangerous and error-prone, whereas `from_bits` is intuitive\n and safe.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n unsafe {\n let _: f32 = std::mem::transmute(1_u32); // where x: u32\n }\n\n // should be:\n let _: f32 = f32::from_bits(1_u32);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "transmute_float_to_int", + "id_span": { + "path": "clippy_lints/src/transmute.rs", + "line": 244 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for transmutes from a float to an integer.\n **Why is this bad?** Transmutes are dangerous and error-prone, whereas `to_bits` is intuitive\n and safe.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n unsafe {\n let _: u32 = std::mem::transmute(1f32);\n }\n\n // should be:\n let _: u32 = 1f32.to_bits();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "transmute_ptr_to_ptr", + "id_span": { + "path": "clippy_lints/src/transmute.rs", + "line": 271 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for transmutes from a pointer to a pointer, or from a reference to a reference.\n\n **Why is this bad?** Transmutes are dangerous, and these can instead be\n written as casts.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let ptr = &1u32 as *const u32;\n unsafe {\n // pointer-to-pointer transmute\n let _: *const f32 = std::mem::transmute(ptr);\n // ref-ref transmute\n let _: &f32 = std::mem::transmute(&1u32);\n }\n // These can be respectively written:\n let _ = ptr as *const f32;\n let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) };\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "unsound_collection_transmute", + "id_span": { + "path": "clippy_lints/src/transmute.rs", + "line": 299 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for transmutes between collections whose types have different ABI, size or alignment.\n\n **Why is this bad?** This is undefined behavior.\n\n **Known problems:** Currently, we cannot know whether a type is a\n collection, so we just lint the ones that come with `std`.\n\n **Example:**\n ```rust\n // different size, therefore likely out-of-bounds memory access\n // You absolutely do not want this in your code!\n unsafe {\n std::mem::transmute::<_, Vec>(vec![2_u16])\n };\n ```\n\n You must always iterate, map and collect the values:\n\n ```rust\n vec![2_u16].into_iter().map(u32::from).collect::>();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "transmuting_null", + "id_span": { + "path": "clippy_lints/src/transmuting_null.rs", + "line": 23 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for transmute calls which would receive a null pointer.\n **Why is this bad?** Transmuting a null pointer is undefined behavior.\n\n **Known problems:** Not all cases can be detected at the moment of this writing.\n For example, variables which hold a null pointer and are then fed to a `transmute`\n call, aren't detectable yet.\n\n **Example:**\n ```rust\n let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) };\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "try_err", + "id_span": { + "path": "clippy_lints/src/try_err.rs", + "line": 43 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for usages of `Err(x)?`.\n **Why is this bad?** The `?` operator is designed to allow calls that\n can fail to be easily chained. For example, `foo()?.bar()` or\n `foo(bar()?)`. Because `Err(x)?` can't be used that way (it will\n always return), it is more clear to write `return Err(x)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn foo(fail: bool) -> Result {\n if fail {\n Err(\"failed\")?;\n }\n Ok(0)\n }\n ```\n Could be written:\n\n ```rust\n fn foo(fail: bool) -> Result {\n if fail {\n return Err(\"failed\".into());\n }\n Ok(0)\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "box_vec", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 66 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for use of `Box>` anywhere in the code. Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.\n\n **Why is this bad?** `Vec` already keeps its contents in a separate area on\n the heap. So if you `Box` it, you just add another level of indirection\n without any benefit whatsoever.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n struct X {\n values: Box>,\n }\n ```\n\n Better:\n\n ```rust,ignore\n struct X {\n values: Vec,\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "vec_box", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 95 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for use of `Vec>` where T: Sized anywhere in the code. Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.\n\n **Why is this bad?** `Vec` already keeps its contents in a separate area on\n the heap. So if you `Box` its contents, you just add another level of indirection.\n\n **Known problems:** Vec> makes sense if T is a large type (see [#3530](https://github.com/rust-lang/rust-clippy/issues/3530),\n 1st comment).\n\n **Example:**\n ```rust\n struct X {\n values: Vec>,\n }\n ```\n\n Better:\n\n ```rust\n struct X {\n values: Vec,\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "option_option", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 133 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for use of `Option>` in function signatures and type definitions\n\n **Why is this bad?** `Option<_>` represents an optional value. `Option>`\n represents an optional optional value which is logically the same thing as an optional\n value but has an unneeded extra level of wrapping.\n\n If you have a case where `Some(Some(_))`, `Some(None)` and `None` are distinct cases,\n consider a custom `enum` instead, with clear names for each case.\n\n **Known problems:** None.\n\n **Example**\n ```rust\n fn get_data() -> Option> {\n None\n }\n ```\n\n Better:\n\n ```rust\n pub enum Contents {\n Data(Vec), // Was Some(Some(Vec))\n NotYetFetched, // Was Some(None)\n None, // Was None\n }\n\n fn get_data() -> Contents {\n Contents::None\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "linkedlist", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 169 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for usage of any `LinkedList`, suggesting to use a `Vec` or a `VecDeque` (formerly called `RingBuf`).\n\n **Why is this bad?** Gankro says:\n\n > The TL;DR of `LinkedList` is that it's built on a massive amount of\n pointers and indirection.\n > It wastes memory, it has terrible cache locality, and is all-around slow.\n `RingBuf`, while\n > \"only\" amortized for push/pop, should be faster in the general case for\n almost every possible\n > workload, and isn't even amortized at all if you can predict the capacity\n you need.\n >\n > `LinkedList`s are only really good if you're doing a lot of merging or\n splitting of lists.\n > This is because they can just mangle some pointers instead of actually\n copying the data. Even\n > if you're doing a lot of insertion in the middle of the list, `RingBuf`\n can still be better\n > because of how expensive it is to seek to the middle of a `LinkedList`.\n\n **Known problems:** False positives – the instances where using a\n `LinkedList` makes sense are few and far between, but they can still happen.\n\n **Example:**\n ```rust\n # use std::collections::LinkedList;\n let x: LinkedList = LinkedList::new();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "borrowed_box", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 193 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for use of `&Box` anywhere in the code. Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.\n\n **Why is this bad?** Any `&Box` can also be a `&T`, which is more\n general.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n fn foo(bar: &Box) { ... }\n ```\n\n Better:\n\n ```rust,ignore\n fn foo(bar: &T) { ... }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "Unspecified" + } + }, + { + "id": "redundant_allocation", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 217 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for use of redundant allocations anywhere in the code.\n **Why is this bad?** Expressions such as `Rc<&T>`, `Rc>`, `Rc>`, `Box<&T>`\n add an unnecessary level of indirection.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::rc::Rc;\n fn foo(bar: Rc<&usize>) {}\n ```\n\n Better:\n\n ```rust\n fn foo(bar: &usize) {}\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "rc_buffer", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 248 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for `Rc` and `Arc` when `T` is a mutable buffer type such as `String` or `Vec`.\n **Why is this bad?** Expressions such as `Rc` usually have no advantage over `Rc`, since\n it is larger and involves an extra level of indirection, and doesn't implement `Borrow`.\n\n While mutating a buffer type would still be possible with `Rc::get_mut()`, it only\n works if there are no additional references yet, which usually defeats the purpose of\n enclosing it in a shared ownership type. Instead, additionally wrapping the inner\n type with an interior mutable container (such as `RefCell` or `Mutex`) would normally\n be used.\n\n **Known problems:** This pattern can be desirable to avoid the overhead of a `RefCell` or `Mutex` for\n cases where mutation only happens before there are any additional references.\n\n **Example:**\n ```rust,ignore\n # use std::rc::Rc;\n fn foo(interned: Rc) { ... }\n ```\n\n Better:\n\n ```rust,ignore\n fn foo(interned: Rc) { ... }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "let_unit_value", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 768 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for binding a unit value.\n **Why is this bad?** A unit value cannot usefully be used anywhere. So\n binding one is kind of pointless.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = {\n 1;\n };\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "unit_cmp", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 849 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for comparisons to unit. This includes all binary comparisons (like `==` and `<`) and asserts.\n\n **Why is this bad?** Unit is always equal to itself, and thus is just a\n clumsily written constant. Mostly this happens when someone accidentally\n adds semicolons at the end of the operands.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn foo() {};\n # fn bar() {};\n # fn baz() {};\n if {\n foo();\n } == {\n bar();\n } {\n baz();\n }\n ```\n is equal to\n ```rust\n # fn foo() {};\n # fn bar() {};\n # fn baz() {};\n {\n foo();\n bar();\n baz();\n }\n ```\n\n For asserts:\n ```rust\n # fn foo() {};\n # fn bar() {};\n assert_eq!({ foo(); }, { bar(); });\n ```\n will always succeed\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "unit_arg", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 922 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for passing a unit value as an argument to a function without using a unit literal (`()`).\n\n **Why is this bad?** This is likely the result of an accidental semicolon.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n foo({\n let a = bar();\n baz(a);\n })\n ```\n", + "applicability": { + "is_multi_suggestion": true, + "applicability": "MachineApplicable" + } + }, + { + "id": "cast_precision_loss", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 1158 + }, + "group": "clippy::pedantic", + "docs": " **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\n rounding errors. This possible rounding is to be expected, so this lint is\n `Allow` by default.\n\n Basically, this warns on casting any integer with 32 or more bits to `f32`\n or any 64-bit integer to `f64`.\n\n **Why is this bad?** It's not bad at all. But in some applications it can be\n helpful to know where precision loss can take place. This lint can help find\n those places in the code.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = u64::MAX;\n x as f64;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "cast_sign_loss", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 1179 + }, + "group": "clippy::pedantic", + "docs": " **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,\n which can be quite surprising in practice. However, as the cast works as\n defined, this lint is `Allow` by default.\n\n **Why is this bad?** Possibly surprising results. You can activate this lint\n as a one-time check to see where numerical wrapping can arise.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let y: i8 = -1;\n y as u128; // will return 18446744073709551615\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "cast_possible_truncation", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 1201 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for casts between numerical types that may truncate large values. This is expected behavior, so the cast is `Allow` by\n default.\n\n **Why is this bad?** In some problem domains, it is good practice to avoid\n truncation. This lint can be activated to help assess where additional\n checks could be beneficial.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn as_u8(x: u64) -> u8 {\n x as u8\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "cast_possible_wrap", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 1224 + }, + "group": "clippy::pedantic", + "docs": " **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,\n i.e., nothing is changed at the bit level, and the binary representation of\n the value is reinterpreted. This can cause wrapping if the value is too big\n for the target signed type. However, the cast works as defined, so this lint\n is `Allow` by default.\n\n **Why is this bad?** While such a cast is not bad in itself, the results can\n be surprising when this is not the intended behavior, as demonstrated by the\n example below.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n u32::MAX as i32; // will yield a value of `-1`\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "cast_lossless", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 1256 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for casts between numerical types that may be replaced by safe conversion functions.\n\n **Why is this bad?** Rust's `as` keyword will perform many kinds of\n conversions, including silently lossy conversions. Conversion functions such\n as `i32::from` will only perform lossless conversions. Using the conversion\n functions prevents conversions from turning into silent lossy conversions if\n the types of the input expressions ever change, and make it easier for\n people reading the code to know that the conversion is lossless.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn as_u64(x: u8) -> u64 {\n x as u64\n }\n ```\n\n Using `::from` would look like this:\n\n ```rust\n fn as_u64(x: u8) -> u64 {\n u64::from(x)\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "unnecessary_cast", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 1281 + }, + "group": "clippy::complexity", + "docs": " **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.\n\n **Why is this bad?** It's just unnecessary.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let _ = 2i32 as i32;\n let _ = 0.5 as f32;\n ```\n\n Better:\n\n ```rust\n let _ = 2_i32;\n let _ = 0.5_f32;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "cast_ptr_alignment", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 1305 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for casts, using `as` or `pointer::cast`, from a less-strictly-aligned pointer to a more-strictly-aligned pointer\n\n **Why is this bad?** Dereferencing the resulting pointer may be undefined\n behavior.\n\n **Known problems:** Using `std::ptr::read_unaligned` and `std::ptr::write_unaligned` or similar\n on the resulting pointer is fine. Is over-zealous: Casts with manual alignment checks or casts like\n u64-> u8 -> u16 can be fine. Miri is able to do a more in-depth analysis.\n\n **Example:**\n ```rust\n let _ = (&1u8 as *const u8) as *const u16;\n let _ = (&mut 1u8 as *mut u8) as *mut u16;\n\n (&1u8 as *const u8).cast::();\n (&mut 1u8 as *mut u8).cast::();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "fn_to_numeric_cast", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 1332 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for casts of function pointers to something other than usize\n **Why is this bad?**\n Casting a function pointer to anything other than usize/isize is not portable across\n architectures, because you end up losing bits if the target type is too small or end up with a\n bunch of extra bits that waste space and add more instructions to the final binary than\n strictly necessary for the problem\n\n Casting to isize also doesn't make sense since there are no signed addresses.\n\n **Example**\n\n ```rust\n // Bad\n fn fun() -> i32 { 1 }\n let a = fun as i64;\n\n // Good\n fn fun2() -> i32 { 1 }\n let a = fun2 as usize;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "fn_to_numeric_cast_with_truncation", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 1362 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for casts of a function pointer to a numeric type not wide enough to store address.\n\n **Why is this bad?**\n Such a cast discards some bits of the function's address. If this is intended, it would be more\n clearly expressed by casting to usize first, then casting the usize to the intended type (with\n a comment) to perform the truncation.\n\n **Example**\n\n ```rust\n // Bad\n fn fn1() -> i16 {\n 1\n };\n let _ = fn1 as i32;\n\n // Better: Cast to usize first, then comment with the reason for the truncation\n fn fn2() -> i16 {\n 1\n };\n let fn_ptr = fn2 as usize;\n let fn_ptr_truncated = fn_ptr as i32;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "type_complexity", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 1897 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for types used in structs, parameters and `let` declarations above a certain complexity threshold.\n\n **Why is this bad?** Too complex types make the code less readable. Consider\n using a `type` definition to simplify them.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::rc::Rc;\n struct Foo {\n inner: Rc>>>,\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "char_lit_as_u8", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 2068 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for expressions where a character literal is cast to `u8` and suggests using a byte literal instead.\n\n **Why is this bad?** In general, casting values to smaller types is\n error-prone and should be avoided where possible. In the particular case of\n converting a character literal to u8, it is easy to avoid by just using a\n byte literal instead. As an added bonus, `b'a'` is even slightly shorter\n than `'a' as u8`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n 'x' as u8\n ```\n\n A better version, using the byte literal:\n\n ```rust,ignore\n b'x'\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "absurd_extreme_comparisons", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 2133 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for comparisons where one side of the relation is either the minimum or maximum value for its type and warns if it involves a\n case that is always true or always false. Only integer and boolean types are\n checked.\n\n **Why is this bad?** An expression like `min <= x` may misleadingly imply\n that it is possible for `x` to be less than the minimum. Expressions like\n `max < x` are probably mistakes.\n\n **Known problems:** For `usize` the size of the current compile target will\n be assumed (e.g., 64 bits on 64 bit systems). This means code that uses such\n a comparison to detect target pointer width will trigger this lint. One can\n use `mem::sizeof` and compare its value or conditional compilation\n attributes\n like `#[cfg(target_pointer_width = \"64\")] ..` instead.\n\n **Example:**\n\n ```rust\n let vec: Vec = Vec::new();\n if vec.len() <= 0 {}\n if 100 > i32::MAX {}\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "invalid_upcast_comparisons", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 2294 + }, + "group": "clippy::pedantic", + "docs": " **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\n necessary. Only integer types are checked.\n\n **Why is this bad?** An expression like `let x : u8 = ...; (x as u32) > 300`\n will mistakenly imply that it is possible for `x` to be outside the range of\n `u8`.\n\n **Known problems:**\n https://github.com/rust-lang/rust-clippy/issues/886\n\n **Example:**\n ```rust\n let x: u8 = 1;\n (x as u32) > 300;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "implicit_hasher", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 2515 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for public `impl` or `fn` missing generalization over different hashers and implicitly defaulting to the default hashing\n algorithm (`SipHash`).\n\n **Why is this bad?** `HashMap` or `HashSet` with custom hashers cannot be\n used with them.\n\n **Known problems:** Suggestions for replacing constructors can contain\n false-positives. Also applying suggestions can require modification of other\n pieces of code, possibly including external crates.\n\n **Example:**\n ```rust\n # use std::collections::HashMap;\n # use std::hash::{Hash, BuildHasher};\n # trait Serialize {};\n impl Serialize for HashMap { }\n\n pub fn foo(map: &mut HashMap) { }\n ```\n could be rewritten as\n ```rust\n # use std::collections::HashMap;\n # use std::hash::{Hash, BuildHasher};\n # trait Serialize {};\n impl Serialize for HashMap { }\n\n pub fn foo(map: &mut HashMap) { }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "cast_ref_to_mut", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 2866 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for casts of `&T` to `&mut T` anywhere in the code.\n **Why is this bad?** It’s basically guaranteed to be undefined behaviour.\n `UnsafeCell` is the only way to obtain aliasable data that is considered\n mutable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n fn x(r: &i32) {\n unsafe {\n *(r as *const _ as *mut _) += 1;\n }\n }\n ```\n\n Instead consider using interior mutability types.\n\n ```rust\n use std::cell::UnsafeCell;\n\n fn x(r: &UnsafeCell) {\n unsafe {\n *r.get() += 1;\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "ptr_as_ptr", + "id_span": { + "path": "clippy_lints/src/types.rs", + "line": 2922 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for `as` casts between raw pointers without changing its mutability,\n namely `*const T` to `*const U` and `*mut T` to `*mut U`.\n\n **Why is this bad?**\n Though `as` casts between raw pointers is not terrible, `pointer::cast` is safer because\n it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let ptr: *const u32 = &42_u32;\n let mut_ptr: *mut u32 = &mut 42_u32;\n let _ = ptr as *const i32;\n let _ = mut_ptr as *mut i32;\n ```\n Use instead:\n ```rust\n let ptr: *const u32 = &42_u32;\n let mut_ptr: *mut u32 = &mut 42_u32;\n let _ = ptr.cast::();\n let _ = mut_ptr.cast::();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "undropped_manually_drops", + "id_span": { + "path": "clippy_lints/src/undropped_manually_drops.rs", + "line": 27 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`.\n **Why is this bad?** The safe `drop` function does not drop the inner value of a `ManuallyDrop`.\n\n **Known problems:** Does not catch cases if the user binds `std::mem::drop`\n to a different name and calls it that way.\n\n **Example:**\n\n ```rust\n struct S;\n drop(std::mem::ManuallyDrop::new(S));\n ```\n Use instead:\n ```rust\n struct S;\n unsafe {\n std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S));\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "invisible_characters", + "id_span": { + "path": "clippy_lints/src/unicode.rs", + "line": 20 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for invisible Unicode characters in the code.\n **Why is this bad?** Having an invisible character in the code makes for all\n sorts of April fools, but otherwise is very much frowned upon.\n\n **Known problems:** None.\n\n **Example:** You don't see it, but there may be a zero-width space or soft hyphen\n some­where in this text.\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "non_ascii_literal", + "id_span": { + "path": "clippy_lints/src/unicode.rs", + "line": 44 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for non-ASCII characters in string literals.\n **Why is this bad?** Yeah, we know, the 90's called and wanted their charset\n back. Even so, there still are editors and other programs out there that\n don't work well with Unicode. So if the code is meant to be used\n internationally, on multiple operating systems, or has other portability\n requirements, activating this lint could be useful.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = String::from(\"€\");\n ```\n Could be written as:\n ```rust\n let x = String::from(\"\\u{20ac}\");\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "unicode_not_nfc", + "id_span": { + "path": "clippy_lints/src/unicode.rs", + "line": 61 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for string literals that contain Unicode in a form that is not equal to its\n [NFC-recomposition](http://www.unicode.org/reports/tr15/#Norm_Forms).\n\n **Why is this bad?** If such a string is compared to another, the results\n may be surprising.\n\n **Known problems** None.\n\n **Example:** You may not see it, but \"à\"\" and \"à\"\" aren't the same string. The\n former when escaped is actually `\"a\\u{300}\"` while the latter is `\"\\u{e0}\"`.\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "unit_return_expecting_ord", + "id_span": { + "path": "clippy_lints/src/unit_return_expecting_ord.rs", + "line": 30 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for functions that expect closures of type Fn(...) -> Ord where the implemented closure returns the unit type.\n The lint also suggests to remove the semi-colon at the end of the statement if present.\n\n **Why is this bad?** Likely, returning the unit type is unintentional, and\n could simply be caused by an extra semi-colon. Since () implements Ord\n it doesn't cause a compilation error.\n This is the same reasoning behind the unit_cmp lint.\n\n **Known problems:** If returning unit is intentional, then there is no\n way of specifying this without triggering needless_return lint\n\n **Example:**\n\n ```rust\n let mut twins = vec!((1, 1), (2, 2));\n twins.sort_by_key(|x| { x.1; });\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "fn_address_comparisons", + "id_span": { + "path": "clippy_lints/src/unnamed_address.rs", + "line": 27 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for comparisons with an address of a function item.\n **Why is this bad?** Function item address is not guaranteed to be unique and could vary\n between different code generation units. Furthermore different function items could have\n the same address after being merged together.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n type F = fn();\n fn a() {}\n let f: F = a;\n if f == a {\n // ...\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "vtable_address_comparisons", + "id_span": { + "path": "clippy_lints/src/unnamed_address.rs", + "line": 51 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for comparisons with an address of a trait vtable.\n **Why is this bad?** Comparing trait objects pointers compares an vtable addresses which\n are not guaranteed to be unique and could vary between different code generation units.\n Furthermore vtables for different types could have the same address after being merged\n together.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,ignore\n let a: Rc = ...\n let b: Rc = ...\n if Rc::ptr_eq(&a, &b) {\n ...\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "unnecessary_sort_by", + "id_span": { + "path": "clippy_lints/src/unnecessary_sort_by.rs", + "line": 41 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Detects uses of `Vec::sort_by` passing in a closure\n which compares the two arguments, either directly or indirectly.\n\n **Why is this bad?**\n It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if\n possible) than to use `Vec::sort_by` and a more complicated\n closure.\n\n **Known problems:**\n If the suggested `Vec::sort_by_key` uses Reverse and it isn't already\n imported by a use statement, then it will need to be added manually.\n\n **Example:**\n\n ```rust\n # struct A;\n # impl A { fn foo(&self) {} }\n # let mut vec: Vec = Vec::new();\n vec.sort_by(|a, b| a.foo().cmp(&b.foo()));\n ```\n Use instead:\n ```rust\n # struct A;\n # impl A { fn foo(&self) {} }\n # let mut vec: Vec = Vec::new();\n vec.sort_by_key(|a| a.foo());\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "unnecessary_wraps", + "id_span": { + "path": "clippy_lints/src/unnecessary_wraps.rs", + "line": 50 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for private functions that only return `Ok` or `Some`.\n **Why is this bad?** It is not meaningful to wrap values when no `None` or `Err` is returned.\n\n **Known problems:** There can be false positives if the function signature is designed to\n fit some external requirement.\n\n **Example:**\n\n ```rust\n fn get_cool_number(a: bool, b: bool) -> Option {\n if a && b {\n return Some(50);\n }\n if a {\n Some(0)\n } else {\n Some(10)\n }\n }\n ```\n Use instead:\n ```rust\n fn get_cool_number(a: bool, b: bool) -> i32 {\n if a && b {\n return 50;\n }\n if a {\n 0\n } else {\n 10\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": true, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "unnested_or_patterns", + "id_span": { + "path": "clippy_lints/src/unnested_or_patterns.rs", + "line": 47 + }, + "group": "clippy::pedantic", + "docs": " **What it does:**\n Checks for unnested or-patterns, e.g., `Some(0) | Some(2)` and\n suggests replacing the pattern with a nested one, `Some(0 | 2)`.\n\n Another way to think of this is that it rewrites patterns in\n *disjunctive normal form (DNF)* into *conjunctive normal form (CNF)*.\n\n **Why is this bad?**\n\n In the example above, `Some` is repeated, which unncessarily complicates the pattern.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n fn main() {\n if let Some(0) | Some(2) = Some(0) {}\n }\n ```\n Use instead:\n ```rust\n #![feature(or_patterns)]\n\n fn main() {\n if let Some(0 | 2) = Some(0) {}\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "unsafe_removed_from_name", + "id_span": { + "path": "clippy_lints/src/unsafe_removed_from_name.rs", + "line": 24 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for imports that remove \"unsafe\" from an item's name.\n\n **Why is this bad?** Renaming makes it less clear which traits and\n structures are unsafe.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n use std::cell::{UnsafeCell as TotallySafeCell};\n\n extern crate crossbeam;\n use crossbeam::{spawn_unsafe as spawn};\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "unused_io_amount", + "id_span": { + "path": "clippy_lints/src/unused_io_amount.rs", + "line": 28 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for unused written/read amount.\n **Why is this bad?** `io::Write::write(_vectored)` and\n `io::Read::read(_vectored)` are not guaranteed to\n process the entire buffer. They return how many bytes were processed, which\n might be smaller\n than a given buffer's length. If you don't need to deal with\n partial-write/read, use\n `write_all`/`read_exact` instead.\n\n **Known problems:** Detects only common patterns.\n\n **Example:**\n ```rust,ignore\n use std::io;\n fn foo(w: &mut W) -> io::Result<()> {\n // must be `w.write_all(b\"foo\")?;`\n w.write(b\"foo\")?;\n Ok(())\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "unused_self", + "id_span": { + "path": "clippy_lints/src/unused_self.rs", + "line": 33 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks methods that contain a `self` argument but don't use it\n **Why is this bad?** It may be clearer to define the method as an associated function instead\n of an instance method if it doesn't require `self`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n struct A;\n impl A {\n fn method(&self) {}\n }\n ```\n\n Could be written:\n\n ```rust,ignore\n struct A;\n impl A {\n fn method() {}\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "unused_unit", + "id_span": { + "path": "clippy_lints/src/unused_unit.rs", + "line": 27 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for unit (`()`) expressions that can be removed.\n **Why is this bad?** Such expressions add no value, but can make the code\n less readable. Depending on formatting they can make a `break` or `return`\n statement look like a function call.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn return_unit() -> () {\n ()\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "unnecessary_unwrap", + "id_span": { + "path": "clippy_lints/src/unwrap.rs", + "line": 40 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for calls of `unwrap[_err]()` that cannot fail.\n **Why is this bad?** Using `if let` or `match` is more idiomatic.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n # let option = Some(0);\n # fn do_something_with(_x: usize) {}\n if option.is_some() {\n do_something_with(option.unwrap())\n }\n ```\n\n Could be written:\n\n ```rust\n # let option = Some(0);\n # fn do_something_with(_x: usize) {}\n if let Some(value) = option {\n do_something_with(value)\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "panicking_unwrap", + "id_span": { + "path": "clippy_lints/src/unwrap.rs", + "line": 63 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Checks for calls of `unwrap[_err]()` that will always fail.\n **Why is this bad?** If panicking is desired, an explicit `panic!()` should be used.\n\n **Known problems:** This lint only checks `if` conditions not assignments.\n So something like `let x: Option<()> = None; x.unwrap();` will not be recognized.\n\n **Example:**\n ```rust\n # let option = Some(0);\n # fn do_something_with(_x: usize) {}\n if option.is_none() {\n do_something_with(option.unwrap())\n }\n ```\n\n This code will always panic. The if condition should probably be inverted.\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "unwrap_in_result", + "id_span": { + "path": "clippy_lints/src/unwrap_in_result.rs", + "line": 47 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for functions of type Result that contain `expect()` or `unwrap()`\n **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.\n\n **Known problems:** This can cause false positives in functions that handle both recoverable and non recoverable errors.\n\n **Example:**\n Before:\n ```rust\n fn divisible_by_3(i_str: String) -> Result<(), String> {\n let i = i_str\n .parse::()\n .expect(\"cannot divide the input by three\");\n\n if i % 3 != 0 {\n Err(\"Number is not divisible by 3\")?\n }\n\n Ok(())\n }\n ```\n\n After:\n ```rust\n fn divisible_by_3(i_str: String) -> Result<(), String> {\n let i = i_str\n .parse::()\n .map_err(|e| format!(\"cannot divide the input by three: {}\", e))?;\n\n if i % 3 != 0 {\n Err(\"Number is not divisible by 3\")?\n }\n\n Ok(())\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "upper_case_acronyms", + "id_span": { + "path": "clippy_lints/src/upper_case_acronyms.rs", + "line": 35 + }, + "group": "clippy::style", + "docs": " **What it does:** Checks for fully capitalized names and optionally names containing a capitalized acronym.\n **Why is this bad?** In CamelCase, acronyms count as one word.\n See [naming conventions](https://rust-lang.github.io/api-guidelines/naming.html#casing-conforms-to-rfc-430-c-case)\n for more.\n\n By default, the lint only triggers on fully-capitalized names.\n You can use the `upper-case-acronyms-aggressive: true` config option to enable linting\n on all camel case names\n\n **Known problems:** When two acronyms are contiguous, the lint can't tell where\n the first acronym ends and the second starts, so it suggests to lowercase all of\n the letters in the second acronym.\n\n **Example:**\n\n ```rust\n struct HTTPResponse;\n ```\n Use instead:\n ```rust\n struct HttpResponse;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "use_self", + "id_span": { + "path": "clippy_lints/src/use_self.rs", + "line": 53 + }, + "group": "clippy::nursery", + "docs": " **What it does:** Checks for unnecessary repetition of structure name when a replacement with `Self` is applicable.\n\n **Why is this bad?** Unnecessary repetition. Mixed use of `Self` and struct\n name\n feels inconsistent.\n\n **Known problems:**\n - Unaddressed false negative in fn bodies of trait implementations\n - False positive with assotiated types in traits (#4140)\n\n **Example:**\n\n ```rust\n struct Foo {}\n impl Foo {\n fn new() -> Foo {\n Foo {}\n }\n }\n ```\n could be\n ```rust\n struct Foo {}\n impl Foo {\n fn new() -> Self {\n Self {}\n }\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "useless_conversion", + "id_span": { + "path": "clippy_lints/src/useless_conversion.rs", + "line": 32 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` calls which uselessly convert to the same type.\n\n **Why is this bad?** Redundant code.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n // format!() returns a `String`\n let s: String = format!(\"hello\").into();\n\n // Good\n let s: String = format!(\"hello\");\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "useless_vec", + "id_span": { + "path": "clippy_lints/src/vec.rs", + "line": 36 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for usage of `&vec![..]` when using `&[..]` would be possible.\n\n **Why is this bad?** This is less efficient.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn foo(my_vec: &[u8]) {}\n\n // Bad\n foo(&vec![1, 2]);\n\n // Good\n foo(&[1, 2]);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "vec_init_then_push", + "id_span": { + "path": "clippy_lints/src/vec_init_then_push.rs", + "line": 32 + }, + "group": "clippy::perf", + "docs": " **What it does:** Checks for calls to `push` immediately after creating a new `Vec`.\n **Why is this bad?** The `vec![]` macro is both more performant and easier to read than\n multiple `push` calls.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let mut v = Vec::new();\n v.push(0);\n ```\n Use instead:\n ```rust\n let v = vec![0];\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "HasPlaceholders" + } + }, + { + "id": "vec_resize_to_zero", + "id_span": { + "path": "clippy_lints/src/vec_resize_to_zero.rs", + "line": 24 + }, + "group": "clippy::correctness", + "docs": " **What it does:** Finds occurrences of `Vec::resize(0, an_int)`\n **Why is this bad?** This is probably an argument inversion mistake.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n vec!(1, 2, 3, 4, 5).resize(0, 5)\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } + }, + { + "id": "verbose_file_reads", + "id_span": { + "path": "clippy_lints/src/verbose_file_reads.rs", + "line": 29 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for use of File::read_to_end and File::read_to_string.\n **Why is this bad?** `fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values.\n See also: [fs::read docs](https://doc.rust-lang.org/std/fs/fn.read.html), [fs::read_to_string docs](https://doc.rust-lang.org/std/fs/fn.read_to_string.html)\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,no_run\n # use std::io::Read;\n # use std::fs::File;\n let mut f = File::open(\"foo.txt\").unwrap();\n let mut bytes = Vec::new();\n f.read_to_end(&mut bytes).unwrap();\n ```\n Can be written more concisely as\n ```rust,no_run\n # use std::fs;\n let mut bytes = fs::read(\"foo.txt\").unwrap();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "wildcard_dependencies", + "id_span": { + "path": "clippy_lints/src/wildcard_dependencies.rs", + "line": 24 + }, + "group": "clippy::cargo", + "docs": " **What it does:** Checks for wildcard dependencies in the `Cargo.toml`.\n **Why is this bad?** [As the edition guide says](https://rust-lang-nursery.github.io/edition-guide/rust-2018/cargo-and-crates-io/crates-io-disallows-wildcard-dependencies.html),\n it is highly unlikely that you work with any possible version of your dependency,\n and wildcard dependencies would cause unnecessary breakage in the ecosystem.\n\n **Known problems:** None.\n\n **Example:**\n\n ```toml\n [dependencies]\n regex = \"*\"\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "enum_glob_use", + "id_span": { + "path": "clippy_lints/src/wildcard_imports.rs", + "line": 32 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for `use Enum::*`.\n **Why is this bad?** It is usually better style to use the prefixed name of\n an enumeration variant, rather than importing variants.\n\n **Known problems:** Old-style enumerations that prefix the variants are\n still around.\n\n **Example:**\n ```rust,ignore\n // Bad\n use std::cmp::Ordering::*;\n foo(Less);\n\n // Good\n use std::cmp::Ordering;\n foo(Ordering::Less)\n ```\n", + "applicability": null + }, + { + "id": "wildcard_imports", + "id_span": { + "path": "clippy_lints/src/wildcard_imports.rs", + "line": 83 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for wildcard imports `use _::*`.\n **Why is this bad?** wildcard imports can pollute the namespace. This is especially bad if\n you try to import something through a wildcard, that already has been imported by name from\n a different source:\n\n ```rust,ignore\n use crate1::foo; // Imports a function named foo\n use crate2::*; // Has a function named foo\n\n foo(); // Calls crate1::foo\n ```\n\n This can lead to confusing error messages at best and to unexpected behavior at worst.\n\n **Exceptions:**\n\n Wildcard imports are allowed from modules named `prelude`. Many crates (including the standard library)\n provide modules named \"prelude\" specifically designed for wildcard import.\n\n `use super::*` is allowed in test modules. This is defined as any module with \"test\" in the name.\n\n These exceptions can be disabled using the `warn-on-all-wildcard-imports` configuration flag.\n\n **Known problems:** If macros are imported through the wildcard, this macro is not included\n by the suggestion and has to be added by hand.\n\n Applying the suggestion when explicit imports of the things imported with a glob import\n exist, may result in `unused_imports` warnings.\n\n **Example:**\n\n ```rust,ignore\n // Bad\n use crate1::*;\n\n foo();\n ```\n\n ```rust,ignore\n // Good\n use crate1::foo;\n\n foo();\n ```\n", + "applicability": null + }, + { + "id": "println_empty_string", + "id_span": { + "path": "clippy_lints/src/write.rs", + "line": 33 + }, + "group": "clippy::style", + "docs": " **What it does:** This lint warns when you use `println!(\"\")` to print a newline.\n\n **Why is this bad?** You should use `println!()`, which is simpler.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n println!(\"\");\n\n // Good\n println!();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "print_with_newline", + "id_span": { + "path": "clippy_lints/src/write.rs", + "line": 57 + }, + "group": "clippy::style", + "docs": " **What it does:** This lint warns when you use `print!()` with a format string that ends in a newline.\n\n **Why is this bad?** You should use `println!()` instead, which appends the\n newline.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let name = \"World\";\n print!(\"Hello {}!\\n\", name);\n ```\n use println!() instead\n ```rust\n # let name = \"World\";\n println!(\"Hello {}!\", name);\n ```\n", + "applicability": { + "is_multi_suggestion": true, + "applicability": "MachineApplicable" + } + }, + { + "id": "print_stdout", + "id_span": { + "path": "clippy_lints/src/write.rs", + "line": 75 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for printing on *stdout*. The purpose of this lint is to catch debugging remnants.\n\n **Why is this bad?** People often print on *stdout* while debugging an\n application and might forget to remove those prints afterward.\n\n **Known problems:** Only catches `print!` and `println!` calls.\n\n **Example:**\n ```rust\n println!(\"Hello world!\");\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "print_stderr", + "id_span": { + "path": "clippy_lints/src/write.rs", + "line": 93 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for printing on *stderr*. The purpose of this lint is to catch debugging remnants.\n\n **Why is this bad?** People often print on *stderr* while debugging an\n application and might forget to remove those prints afterward.\n\n **Known problems:** Only catches `eprint!` and `eprintln!` calls.\n\n **Example:**\n ```rust\n eprintln!(\"Hello world!\");\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "use_debug", + "id_span": { + "path": "clippy_lints/src/write.rs", + "line": 110 + }, + "group": "clippy::restriction", + "docs": " **What it does:** Checks for use of `Debug` formatting. The purpose of this lint is to catch debugging remnants.\n\n **Why is this bad?** The purpose of the `Debug` trait is to facilitate\n debugging Rust code. It should not be used in user-facing output.\n\n **Example:**\n ```rust\n # let foo = \"bar\";\n println!(\"{:?}\", foo);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "print_literal", + "id_span": { + "path": "clippy_lints/src/write.rs", + "line": 133 + }, + "group": "clippy::style", + "docs": " **What it does:** This lint warns about the use of literals as `print!`/`println!` args.\n **Why is this bad?** Using literals as `println!` args is inefficient\n (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary\n (i.e., just put the literal in the format string)\n\n **Known problems:** Will also warn with macro calls as arguments that expand to literals\n -- e.g., `println!(\"{}\", env!(\"FOO\"))`.\n\n **Example:**\n ```rust\n println!(\"{}\", \"foo\");\n ```\n use the literal without formatting:\n ```rust\n println!(\"foo\");\n ```\n", + "applicability": null + }, + { + "id": "writeln_empty_string", + "id_span": { + "path": "clippy_lints/src/write.rs", + "line": 156 + }, + "group": "clippy::style", + "docs": " **What it does:** This lint warns when you use `writeln!(buf, \"\")` to print a newline.\n\n **Why is this bad?** You should use `writeln!(buf)`, which is simpler.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::fmt::Write;\n # let mut buf = String::new();\n // Bad\n writeln!(buf, \"\");\n\n // Good\n writeln!(buf);\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "write_with_newline", + "id_span": { + "path": "clippy_lints/src/write.rs", + "line": 182 + }, + "group": "clippy::style", + "docs": " **What it does:** This lint warns when you use `write!()` with a format string that\n ends in a newline.\n\n **Why is this bad?** You should use `writeln!()` instead, which appends the\n newline.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::fmt::Write;\n # let mut buf = String::new();\n # let name = \"World\";\n // Bad\n write!(buf, \"Hello {}!\\n\", name);\n\n // Good\n writeln!(buf, \"Hello {}!\", name);\n ```\n", + "applicability": { + "is_multi_suggestion": true, + "applicability": "MachineApplicable" + } + }, + { + "id": "write_literal", + "id_span": { + "path": "clippy_lints/src/write.rs", + "line": 207 + }, + "group": "clippy::style", + "docs": " **What it does:** This lint warns about the use of literals as `write!`/`writeln!` args.\n **Why is this bad?** Using literals as `writeln!` args is inefficient\n (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary\n (i.e., just put the literal in the format string)\n\n **Known problems:** Will also warn with macro calls as arguments that expand to literals\n -- e.g., `writeln!(buf, \"{}\", env!(\"FOO\"))`.\n\n **Example:**\n ```rust\n # use std::fmt::Write;\n # let mut buf = String::new();\n // Bad\n writeln!(buf, \"{}\", \"foo\");\n\n // Good\n writeln!(buf, \"foo\");\n ```\n", + "applicability": null + }, + { + "id": "zero_divided_by_zero", + "id_span": { + "path": "clippy_lints/src/zero_div_zero.rs", + "line": 23 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for `0.0 / 0.0`.\n **Why is this bad?** It's less readable than `f32::NAN` or `f64::NAN`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n let nan = 0.0f32 / 0.0;\n\n // Good\n let nan = f32::NAN;\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + }, + { + "id": "zero_sized_map_values", + "id_span": { + "path": "clippy_lints/src/zero_sized_map_values.rs", + "line": 37 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for maps with zero-sized value types anywhere in the code.\n **Why is this bad?** Since there is only a single value for a zero-sized type, a map\n containing zero sized values is effectively a set. Using a set in that case improves\n readability and communicates intent more clearly.\n\n **Known problems:**\n * A zero-sized type cannot be recovered later if it contains private fields.\n * This lints the signature of public items\n\n **Example:**\n\n ```rust\n # use std::collections::HashMap;\n fn unique_words(text: &str) -> HashMap<&str, ()> {\n todo!();\n }\n ```\n Use instead:\n ```rust\n # use std::collections::HashSet;\n fn unique_words(text: &str) -> HashSet<&str> {\n todo!();\n }\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } + } +] From 35844d0a48e0a644072ae81857782a6e0bd54740 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 6 Mar 2021 01:11:41 +0100 Subject: [PATCH 56/67] Metadata collection: Resolve lint from locals --- .../internal_lints/metadata_collector.rs | 82 +- clippy_utils/src/paths.rs | 2 +- metadata_collection.json | 1184 +++++++++-------- 3 files changed, 699 insertions(+), 569 deletions(-) diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index b2cc637227790..0425689b0ddd9 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -15,8 +15,6 @@ // # Applicability // - TODO xFrednet 2021-01-17: Find lint emit and collect applicability -// - TODO xFrednet 2021-02-28: 1x reference to closure -// - See clippy_lints/src/needless_pass_by_value.rs@NeedlessPassByValue::check_fn // - TODO xFrednet 2021-02-28: 4x weird emission forwarding // - See clippy_lints/src/enum_variants.rs@EnumVariantNames::check_name // - TODO xFrednet 2021-02-28: 6x emission forwarding with local that is initializes from @@ -26,14 +24,12 @@ // - See clippy_lints/src/misc.rs@check_binary // - TODO xFrednet 2021-02-28: 2x lint from local from method call // - See clippy_lints/src/non_copy_const.rs@lint -// - TODO xFrednet 2021-02-28: 20x lint from local -// - See clippy_lints/src/map_unit_fn.rs@lint_map_unit_fn // # NITs // - TODO xFrednet 2021-02-13: Collect depreciations and maybe renames use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::{self as hir, intravisit, intravisit::Visitor, ExprKind, Item, ItemKind, Mutability, QPath}; +use rustc_hir::{self as hir, intravisit, intravisit::Visitor, ExprKind, Item, ItemKind, Mutability, QPath, def::DefKind}; use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId}; use rustc_middle::hir::map::Map; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -49,7 +45,7 @@ use crate::utils::{ }; /// This is the output file of the lint collector. -const OUTPUT_FILE: &str = "metadata_collection.json"; +const OUTPUT_FILE: &str = "../metadata_collection.json"; /// These lints are excluded from the export. const BLACK_LISTED_LINTS: [&str; 2] = ["lint_author", "deep_code_inspection"]; /// These groups will be ignored by the lint group matcher. This is useful for collections like @@ -270,12 +266,16 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { /// ``` fn check_expr(&mut self, cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) { if let Some(args) = match_lint_emission(cx, expr) { - if let Some((lint_name, applicability, is_multi_part)) = extract_complex_emission_info(cx, args) { + let mut emission_info = extract_complex_emission_info(cx, args); + if emission_info.is_empty() { + lint_collection_error_span(cx, expr.span, "Look, here ... I have no clue what todo with it"); + return; + } + + for (lint_name, applicability, is_multi_part) in emission_info.drain(..) { let app_info = self.applicability_into.entry(lint_name).or_default(); app_info.applicability = applicability; app_info.is_multi_suggestion = is_multi_part; - } else { - lint_collection_error_span(cx, expr.span, "Look, here ... I have no clue what todo with it"); } } } @@ -380,8 +380,8 @@ fn match_lint_emission<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) fn extract_complex_emission_info<'hir>( cx: &LateContext<'hir>, args: &'hir [hir::Expr<'hir>], -) -> Option<(String, Option, bool)> { - let mut lint_name = None; +) -> Vec<(String, Option, bool)> { + let mut lints= Vec::new(); let mut applicability = None; let mut multi_part = false; @@ -390,9 +390,8 @@ fn extract_complex_emission_info<'hir>( if match_type(cx, arg_ty, &paths::LINT) { // If we found the lint arg, extract the lint name - if let ExprKind::Path(ref lint_path) = arg.kind { - lint_name = Some(last_path_segment(lint_path).ident.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() { @@ -402,7 +401,14 @@ fn extract_complex_emission_info<'hir>( } } - lint_name.map(|lint_name| (sym_to_string(lint_name).to_ascii_lowercase(), applicability, multi_part)) + lints.drain(..).map(|lint_name| (lint_name, applicability.clone(), multi_part)).collect() +} + +/// Resolves the possible lints that this expression could reference +fn resolve_lints(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Vec { + let mut resolver = LintResolver::new(cx); + resolver.visit_expr(expr); + resolver.lints } /// This function tries to resolve the linked applicability to the given expression. @@ -426,6 +432,50 @@ fn check_is_multi_part(cx: &LateContext<'hir>, closure_expr: &'hir hir::Expr<'hi false } +struct LintResolver<'a, 'hir> { + cx: &'a LateContext<'hir>, + lints: Vec, +} + +impl<'a, 'hir> LintResolver<'a, 'hir> { + fn new(cx: &'a LateContext<'hir>) -> Self { + Self { + cx, + lints: Vec::::default(), + } + } +} + +impl<'a, 'hir> intravisit::Visitor<'hir> for LintResolver<'a, 'hir> { + type Map = Map<'hir>; + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + 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>, @@ -488,7 +538,7 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityResolver<'a, 'hir> { } } -/// This returns the parent local node if the expression is a reference to +/// 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 { diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index a6292b87768ab..1f19724ad250b 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -83,7 +83,7 @@ pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; #[cfg(feature = "internal-lints")] pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"]; pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"]; -#[cfg(feature = "internal-lints")] +#[cfg(any(feature = "internal-lints", feature = "metadata-collector-lint"))] pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"]; pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"]; pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"]; diff --git a/metadata_collection.json b/metadata_collection.json index 507752a782cf3..f6f72ffa5fa9c 100644 --- a/metadata_collection.json +++ b/metadata_collection.json @@ -2,7 +2,7 @@ { "id": "approx_constant", "id_span": { - "path": "clippy_lints/src/approx_const.rs", + "path": "src/approx_const.rs", "line": 34 }, "group": "clippy::correctness", @@ -15,7 +15,7 @@ { "id": "integer_arithmetic", "id_span": { - "path": "clippy_lints/src/arithmetic.rs", + "path": "src/arithmetic.rs", "line": 28 }, "group": "clippy::restriction", @@ -28,7 +28,7 @@ { "id": "float_arithmetic", "id_span": { - "path": "clippy_lints/src/arithmetic.rs", + "path": "src/arithmetic.rs", "line": 46 }, "group": "clippy::restriction", @@ -41,7 +41,7 @@ { "id": "as_conversions", "id_span": { - "path": "clippy_lints/src/as_conversions.rs", + "path": "src/as_conversions.rs", "line": 42 }, "group": "clippy::restriction", @@ -54,7 +54,7 @@ { "id": "inline_asm_x86_intel_syntax", "id_span": { - "path": "clippy_lints/src/asm_syntax.rs", + "path": "src/asm_syntax.rs", "line": 78 }, "group": "clippy::restriction", @@ -64,7 +64,7 @@ { "id": "inline_asm_x86_att_syntax", "id_span": { - "path": "clippy_lints/src/asm_syntax.rs", + "path": "src/asm_syntax.rs", "line": 114 }, "group": "clippy::restriction", @@ -74,7 +74,7 @@ { "id": "assertions_on_constants", "id_span": { - "path": "clippy_lints/src/assertions_on_constants.rs", + "path": "src/assertions_on_constants.rs", "line": 23 }, "group": "clippy::style", @@ -87,7 +87,7 @@ { "id": "assign_op_pattern", "id_span": { - "path": "clippy_lints/src/assign_ops.rs", + "path": "src/assign_ops.rs", "line": 33 }, "group": "clippy::style", @@ -100,7 +100,7 @@ { "id": "misrefactored_assign_op", "id_span": { - "path": "clippy_lints/src/assign_ops.rs", + "path": "src/assign_ops.rs", "line": 56 }, "group": "clippy::complexity", @@ -113,7 +113,7 @@ { "id": "async_yields_async", "id_span": { - "path": "clippy_lints/src/async_yields_async.rs", + "path": "src/async_yields_async.rs", "line": 36 }, "group": "clippy::correctness", @@ -126,7 +126,7 @@ { "id": "invalid_atomic_ordering", "id_span": { - "path": "clippy_lints/src/atomic_ordering.rs", + "path": "src/atomic_ordering.rs", "line": 47 }, "group": "clippy::correctness", @@ -139,7 +139,7 @@ { "id": "inline_always", "id_span": { - "path": "clippy_lints/src/attrs.rs", + "path": "src/attrs.rs", "line": 65 }, "group": "clippy::pedantic", @@ -152,7 +152,7 @@ { "id": "useless_attribute", "id_span": { - "path": "clippy_lints/src/attrs.rs", + "path": "src/attrs.rs", "line": 99 }, "group": "clippy::correctness", @@ -165,7 +165,7 @@ { "id": "deprecated_semver", "id_span": { - "path": "clippy_lints/src/attrs.rs", + "path": "src/attrs.rs", "line": 118 }, "group": "clippy::correctness", @@ -178,7 +178,7 @@ { "id": "empty_line_after_outer_attr", "id_span": { - "path": "clippy_lints/src/attrs.rs", + "path": "src/attrs.rs", "line": 153 }, "group": "clippy::nursery", @@ -191,7 +191,7 @@ { "id": "blanket_clippy_restriction_lints", "id_span": { - "path": "clippy_lints/src/attrs.rs", + "path": "src/attrs.rs", "line": 176 }, "group": "clippy::style", @@ -204,7 +204,7 @@ { "id": "deprecated_cfg_attr", "id_span": { - "path": "clippy_lints/src/attrs.rs", + "path": "src/attrs.rs", "line": 205 }, "group": "clippy::complexity", @@ -217,7 +217,7 @@ { "id": "mismatched_target_os", "id_span": { - "path": "clippy_lints/src/attrs.rs", + "path": "src/attrs.rs", "line": 238 }, "group": "clippy::correctness", @@ -230,7 +230,7 @@ { "id": "await_holding_lock", "id_span": { - "path": "clippy_lints/src/await_holding_invalid.rs", + "path": "src/await_holding_invalid.rs", "line": 47 }, "group": "clippy::pedantic", @@ -243,7 +243,7 @@ { "id": "await_holding_refcell_ref", "id_span": { - "path": "clippy_lints/src/await_holding_invalid.rs", + "path": "src/await_holding_invalid.rs", "line": 86 }, "group": "clippy::pedantic", @@ -256,7 +256,7 @@ { "id": "bad_bit_mask", "id_span": { - "path": "clippy_lints/src/bit_mask.rs", + "path": "src/bit_mask.rs", "line": 44 }, "group": "clippy::correctness", @@ -269,7 +269,7 @@ { "id": "ineffective_bit_mask", "id_span": { - "path": "clippy_lints/src/bit_mask.rs", + "path": "src/bit_mask.rs", "line": 73 }, "group": "clippy::correctness", @@ -282,7 +282,7 @@ { "id": "verbose_bit_mask", "id_span": { - "path": "clippy_lints/src/bit_mask.rs", + "path": "src/bit_mask.rs", "line": 92 }, "group": "clippy::pedantic", @@ -295,7 +295,7 @@ { "id": "blacklisted_name", "id_span": { - "path": "clippy_lints/src/blacklisted_name.rs", + "path": "src/blacklisted_name.rs", "line": 20 }, "group": "clippy::style", @@ -308,7 +308,7 @@ { "id": "blocks_in_if_conditions", "id_span": { - "path": "clippy_lints/src/blocks_in_if_conditions.rs", + "path": "src/blocks_in_if_conditions.rs", "line": 42 }, "group": "clippy::style", @@ -321,7 +321,7 @@ { "id": "nonminimal_bool", "id_span": { - "path": "clippy_lints/src/booleans.rs", + "path": "src/booleans.rs", "line": 31 }, "group": "clippy::complexity", @@ -334,7 +334,7 @@ { "id": "logic_bug", "id_span": { - "path": "clippy_lints/src/booleans.rs", + "path": "src/booleans.rs", "line": 49 }, "group": "clippy::correctness", @@ -347,10 +347,10 @@ { "id": "naive_bytecount", "id_span": { - "path": "clippy_lints/src/bytecount.rs", + "path": "src/bytecount.rs", "line": 30 }, - "group": "clippy::perf", + "group": "clippy::pedantic", "docs": " **What it does:** Checks for naive byte counts\n **Why is this bad?** The [`bytecount`](https://crates.io/crates/bytecount)\n crate has methods to count your bytes faster, especially for large slices.\n\n **Known problems:** If you have predominantly small slices, the\n `bytecount::count(..)` method may actually be slower. However, if you can\n ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be\n faster in those cases.\n\n **Example:**\n\n ```rust\n # let vec = vec![1_u8];\n &vec.iter().filter(|x| **x == 0u8).count(); // use bytecount::count instead\n ```\n", "applicability": { "is_multi_suggestion": false, @@ -360,7 +360,7 @@ { "id": "cargo_common_metadata", "id_span": { - "path": "clippy_lints/src/cargo_common_metadata.rs", + "path": "src/cargo_common_metadata.rs", "line": 49 }, "group": "clippy::cargo", @@ -373,7 +373,7 @@ { "id": "case_sensitive_file_extension_comparisons", "id_span": { - "path": "clippy_lints/src/case_sensitive_file_extension_comparisons.rs", + "path": "src/case_sensitive_file_extension_comparisons.rs", "line": 34 }, "group": "clippy::pedantic", @@ -386,7 +386,7 @@ { "id": "checked_conversions", "id_span": { - "path": "clippy_lints/src/checked_conversions.rs", + "path": "src/checked_conversions.rs", "line": 40 }, "group": "clippy::pedantic", @@ -399,7 +399,7 @@ { "id": "cognitive_complexity", "id_span": { - "path": "clippy_lints/src/cognitive_complexity.rs", + "path": "src/cognitive_complexity.rs", "line": 24 }, "group": "clippy::nursery", @@ -412,7 +412,7 @@ { "id": "collapsible_if", "id_span": { - "path": "clippy_lints/src/collapsible_if.rs", + "path": "src/collapsible_if.rs", "line": 50 }, "group": "clippy::style", @@ -425,7 +425,7 @@ { "id": "collapsible_else_if", "id_span": { - "path": "clippy_lints/src/collapsible_if.rs", + "path": "src/collapsible_if.rs", "line": 85 }, "group": "clippy::style", @@ -438,7 +438,7 @@ { "id": "collapsible_match", "id_span": { - "path": "clippy_lints/src/collapsible_match.rs", + "path": "src/collapsible_match.rs", "line": 44 }, "group": "clippy::style", @@ -451,7 +451,7 @@ { "id": "comparison_chain", "id_span": { - "path": "clippy_lints/src/comparison_chain.rs", + "path": "src/comparison_chain.rs", "line": 49 }, "group": "clippy::style", @@ -464,7 +464,7 @@ { "id": "ifs_same_cond", "id_span": { - "path": "clippy_lints/src/copies.rs", + "path": "src/copies.rs", "line": 33 }, "group": "clippy::correctness", @@ -477,7 +477,7 @@ { "id": "same_functions_in_if_condition", "id_span": { - "path": "clippy_lints/src/copies.rs", + "path": "src/copies.rs", "line": 80 }, "group": "clippy::pedantic", @@ -490,7 +490,7 @@ { "id": "if_same_then_else", "id_span": { - "path": "clippy_lints/src/copies.rs", + "path": "src/copies.rs", "line": 101 }, "group": "clippy::correctness", @@ -503,7 +503,7 @@ { "id": "copy_iterator", "id_span": { - "path": "clippy_lints/src/copy_iterator.rs", + "path": "src/copy_iterator.rs", "line": 27 }, "group": "clippy::pedantic", @@ -516,7 +516,7 @@ { "id": "create_dir", "id_span": { - "path": "clippy_lints/src/create_dir.rs", + "path": "src/create_dir.rs", "line": 24 }, "group": "clippy::restriction", @@ -529,7 +529,7 @@ { "id": "dbg_macro", "id_span": { - "path": "clippy_lints/src/dbg_macro.rs", + "path": "src/dbg_macro.rs", "line": 25 }, "group": "clippy::restriction", @@ -542,7 +542,7 @@ { "id": "default_trait_access", "id_span": { - "path": "clippy_lints/src/default.rs", + "path": "src/default.rs", "line": 33 }, "group": "clippy::pedantic", @@ -555,7 +555,7 @@ { "id": "field_reassign_with_default", "id_span": { - "path": "clippy_lints/src/default.rs", + "path": "src/default.rs", "line": 63 }, "group": "clippy::style", @@ -568,7 +568,7 @@ { "id": "default_numeric_fallback", "id_span": { - "path": "clippy_lints/src/default_numeric_fallback.rs", + "path": "src/default_numeric_fallback.rs", "line": 44 }, "group": "clippy::restriction", @@ -581,7 +581,7 @@ { "id": "explicit_deref_methods", "id_span": { - "path": "clippy_lints/src/dereference.rs", + "path": "src/dereference.rs", "line": 32 }, "group": "clippy::pedantic", @@ -594,7 +594,7 @@ { "id": "derive_hash_xor_eq", "id_span": { - "path": "clippy_lints/src/derive.rs", + "path": "src/derive.rs", "line": 42 }, "group": "clippy::correctness", @@ -607,7 +607,7 @@ { "id": "derive_ord_xor_partial_ord", "id_span": { - "path": "clippy_lints/src/derive.rs", + "path": "src/derive.rs", "line": 93 }, "group": "clippy::correctness", @@ -620,7 +620,7 @@ { "id": "expl_impl_clone_on_copy", "id_span": { - "path": "clippy_lints/src/derive.rs", + "path": "src/derive.rs", "line": 119 }, "group": "clippy::pedantic", @@ -633,7 +633,7 @@ { "id": "unsafe_derive_deserialize", "id_span": { - "path": "clippy_lints/src/derive.rs", + "path": "src/derive.rs", "line": 153 }, "group": "clippy::pedantic", @@ -646,7 +646,7 @@ { "id": "disallowed_method", "id_span": { - "path": "clippy_lints/src/disallowed_method.rs", + "path": "src/disallowed_method.rs", "line": 46 }, "group": "clippy::nursery", @@ -659,7 +659,7 @@ { "id": "doc_markdown", "id_span": { - "path": "clippy_lints/src/doc.rs", + "path": "src/doc.rs", "line": 63 }, "group": "clippy::pedantic", @@ -672,7 +672,7 @@ { "id": "missing_safety_doc", "id_span": { - "path": "clippy_lints/src/doc.rs", + "path": "src/doc.rs", "line": 97 }, "group": "clippy::style", @@ -685,7 +685,7 @@ { "id": "missing_errors_doc", "id_span": { - "path": "clippy_lints/src/doc.rs", + "path": "src/doc.rs", "line": 126 }, "group": "clippy::pedantic", @@ -698,7 +698,7 @@ { "id": "missing_panics_doc", "id_span": { - "path": "clippy_lints/src/doc.rs", + "path": "src/doc.rs", "line": 157 }, "group": "clippy::pedantic", @@ -711,7 +711,7 @@ { "id": "needless_doctest_main", "id_span": { - "path": "clippy_lints/src/doc.rs", + "path": "src/doc.rs", "line": 185 }, "group": "clippy::style", @@ -724,7 +724,7 @@ { "id": "double_comparisons", "id_span": { - "path": "clippy_lints/src/double_comparison.rs", + "path": "src/double_comparison.rs", "line": 33 }, "group": "clippy::complexity", @@ -737,7 +737,7 @@ { "id": "double_parens", "id_span": { - "path": "clippy_lints/src/double_parens.rs", + "path": "src/double_parens.rs", "line": 35 }, "group": "clippy::complexity", @@ -750,7 +750,7 @@ { "id": "drop_ref", "id_span": { - "path": "clippy_lints/src/drop_forget_ref.rs", + "path": "src/drop_forget_ref.rs", "line": 26 }, "group": "clippy::correctness", @@ -760,7 +760,7 @@ { "id": "forget_ref", "id_span": { - "path": "clippy_lints/src/drop_forget_ref.rs", + "path": "src/drop_forget_ref.rs", "line": 47 }, "group": "clippy::correctness", @@ -770,7 +770,7 @@ { "id": "drop_copy", "id_span": { - "path": "clippy_lints/src/drop_forget_ref.rs", + "path": "src/drop_forget_ref.rs", "line": 68 }, "group": "clippy::correctness", @@ -780,7 +780,7 @@ { "id": "forget_copy", "id_span": { - "path": "clippy_lints/src/drop_forget_ref.rs", + "path": "src/drop_forget_ref.rs", "line": 95 }, "group": "clippy::correctness", @@ -790,7 +790,7 @@ { "id": "duration_subsec", "id_span": { - "path": "clippy_lints/src/duration_subsec.rs", + "path": "src/duration_subsec.rs", "line": 34 }, "group": "clippy::complexity", @@ -803,7 +803,7 @@ { "id": "else_if_without_else", "id_span": { - "path": "clippy_lints/src/else_if_without_else.rs", + "path": "src/else_if_without_else.rs", "line": 44 }, "group": "clippy::restriction", @@ -816,7 +816,7 @@ { "id": "empty_enum", "id_span": { - "path": "clippy_lints/src/empty_enum.rs", + "path": "src/empty_enum.rs", "line": 38 }, "group": "clippy::pedantic", @@ -829,7 +829,7 @@ { "id": "map_entry", "id_span": { - "path": "clippy_lints/src/entry.rs", + "path": "src/entry.rs", "line": 48 }, "group": "clippy::perf", @@ -842,7 +842,7 @@ { "id": "enum_clike_unportable_variant", "id_span": { - "path": "clippy_lints/src/enum_clike.rs", + "path": "src/enum_clike.rs", "line": 31 }, "group": "clippy::correctness", @@ -855,7 +855,7 @@ { "id": "enum_variant_names", "id_span": { - "path": "clippy_lints/src/enum_variants.rs", + "path": "src/enum_variants.rs", "line": 36 }, "group": "clippy::style", @@ -865,7 +865,7 @@ { "id": "pub_enum_variant_names", "id_span": { - "path": "clippy_lints/src/enum_variants.rs", + "path": "src/enum_variants.rs", "line": 66 }, "group": "clippy::pedantic", @@ -875,7 +875,7 @@ { "id": "module_name_repetitions", "id_span": { - "path": "clippy_lints/src/enum_variants.rs", + "path": "src/enum_variants.rs", "line": 91 }, "group": "clippy::pedantic", @@ -888,7 +888,7 @@ { "id": "module_inception", "id_span": { - "path": "clippy_lints/src/enum_variants.rs", + "path": "src/enum_variants.rs", "line": 121 }, "group": "clippy::style", @@ -901,7 +901,7 @@ { "id": "eq_op", "id_span": { - "path": "clippy_lints/src/eq_op.rs", + "path": "src/eq_op.rs", "line": 34 }, "group": "clippy::correctness", @@ -914,7 +914,7 @@ { "id": "op_ref", "id_span": { - "path": "clippy_lints/src/eq_op.rs", + "path": "src/eq_op.rs", "line": 56 }, "group": "clippy::style", @@ -927,7 +927,7 @@ { "id": "erasing_op", "id_span": { - "path": "clippy_lints/src/erasing_op.rs", + "path": "src/erasing_op.rs", "line": 25 }, "group": "clippy::correctness", @@ -940,7 +940,7 @@ { "id": "boxed_local", "id_span": { - "path": "clippy_lints/src/escape.rs", + "path": "src/escape.rs", "line": 43 }, "group": "clippy::perf", @@ -953,7 +953,7 @@ { "id": "redundant_closure", "id_span": { - "path": "clippy_lints/src/eta_reduction.rs", + "path": "src/eta_reduction.rs", "line": 37 }, "group": "clippy::style", @@ -966,7 +966,7 @@ { "id": "redundant_closure_for_method_calls", "id_span": { - "path": "clippy_lints/src/eta_reduction.rs", + "path": "src/eta_reduction.rs", "line": 61 }, "group": "clippy::pedantic", @@ -979,7 +979,7 @@ { "id": "eval_order_dependence", "id_span": { - "path": "clippy_lints/src/eval_order_dependence.rs", + "path": "src/eval_order_dependence.rs", "line": 38 }, "group": "clippy::complexity", @@ -992,7 +992,7 @@ { "id": "diverging_sub_expression", "id_span": { - "path": "clippy_lints/src/eval_order_dependence.rs", + "path": "src/eval_order_dependence.rs", "line": 62 }, "group": "clippy::complexity", @@ -1005,7 +1005,7 @@ { "id": "struct_excessive_bools", "id_span": { - "path": "clippy_lints/src/excessive_bools.rs", + "path": "src/excessive_bools.rs", "line": 40 }, "group": "clippy::pedantic", @@ -1018,7 +1018,7 @@ { "id": "fn_params_excessive_bools", "id_span": { - "path": "clippy_lints/src/excessive_bools.rs", + "path": "src/excessive_bools.rs", "line": 78 }, "group": "clippy::pedantic", @@ -1031,27 +1031,33 @@ { "id": "exhaustive_enums", "id_span": { - "path": "clippy_lints/src/exhaustive_items.rs", + "path": "src/exhaustive_items.rs", "line": 34 }, "group": "clippy::restriction", "docs": " **What it does:** Warns on any exported `enum`s that are not tagged `#[non_exhaustive]`\n **Why is this bad?** Exhaustive enums are typically fine, but a project which does\n not wish to make a stability commitment around exported enums may wish to\n disable them by default.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n enum Foo {\n Bar,\n Baz\n }\n ```\n Use instead:\n ```rust\n #[non_exhaustive]\n enum Foo {\n Bar,\n Baz\n }\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } }, { "id": "exhaustive_structs", "id_span": { - "path": "clippy_lints/src/exhaustive_items.rs", + "path": "src/exhaustive_items.rs", "line": 64 }, "group": "clippy::restriction", "docs": " **What it does:** Warns on any exported `structs`s that are not tagged `#[non_exhaustive]`\n **Why is this bad?** Exhaustive structs are typically fine, but a project which does\n not wish to make a stability commitment around exported structs may wish to\n disable them by default.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n struct Foo {\n bar: u8,\n baz: String,\n }\n ```\n Use instead:\n ```rust\n #[non_exhaustive]\n struct Foo {\n bar: u8,\n baz: String,\n }\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": "MaybeIncorrect" + } }, { "id": "exit", "id_span": { - "path": "clippy_lints/src/exit.rs", + "path": "src/exit.rs", "line": 20 }, "group": "clippy::restriction", @@ -1064,7 +1070,7 @@ { "id": "explicit_write", "id_span": { - "path": "clippy_lints/src/explicit_write.rs", + "path": "src/explicit_write.rs", "line": 25 }, "group": "clippy::complexity", @@ -1077,7 +1083,7 @@ { "id": "fallible_impl_from", "id_span": { - "path": "clippy_lints/src/fallible_impl_from.rs", + "path": "src/fallible_impl_from.rs", "line": 45 }, "group": "clippy::nursery", @@ -1090,7 +1096,7 @@ { "id": "float_equality_without_abs", "id_span": { - "path": "clippy_lints/src/float_equality_without_abs.rs", + "path": "src/float_equality_without_abs.rs", "line": 37 }, "group": "clippy::correctness", @@ -1103,7 +1109,7 @@ { "id": "excessive_precision", "id_span": { - "path": "clippy_lints/src/float_literal.rs", + "path": "src/float_literal.rs", "line": 30 }, "group": "clippy::style", @@ -1116,7 +1122,7 @@ { "id": "lossy_float_literal", "id_span": { - "path": "clippy_lints/src/float_literal.rs", + "path": "src/float_literal.rs", "line": 54 }, "group": "clippy::restriction", @@ -1129,7 +1135,7 @@ { "id": "imprecise_flops", "id_span": { - "path": "clippy_lints/src/floating_point_arithmetic.rs", + "path": "src/floating_point_arithmetic.rs", "line": 45 }, "group": "clippy::nursery", @@ -1142,7 +1148,7 @@ { "id": "suboptimal_flops", "id_span": { - "path": "clippy_lints/src/floating_point_arithmetic.rs", + "path": "src/floating_point_arithmetic.rs", "line": 102 }, "group": "clippy::nursery", @@ -1155,7 +1161,7 @@ { "id": "useless_format", "id_span": { - "path": "clippy_lints/src/format.rs", + "path": "src/format.rs", "line": 37 }, "group": "clippy::complexity", @@ -1168,7 +1174,7 @@ { "id": "suspicious_assignment_formatting", "id_span": { - "path": "clippy_lints/src/formatting.rs", + "path": "src/formatting.rs", "line": 22 }, "group": "clippy::style", @@ -1181,7 +1187,7 @@ { "id": "suspicious_unary_op_formatting", "id_span": { - "path": "clippy_lints/src/formatting.rs", + "path": "src/formatting.rs", "line": 44 }, "group": "clippy::style", @@ -1194,7 +1200,7 @@ { "id": "suspicious_else_formatting", "id_span": { - "path": "clippy_lints/src/formatting.rs", + "path": "src/formatting.rs", "line": 80 }, "group": "clippy::style", @@ -1207,7 +1213,7 @@ { "id": "possible_missing_comma", "id_span": { - "path": "clippy_lints/src/formatting.rs", + "path": "src/formatting.rs", "line": 100 }, "group": "clippy::correctness", @@ -1220,7 +1226,7 @@ { "id": "from_over_into", "id_span": { - "path": "clippy_lints/src/from_over_into.rs", + "path": "src/from_over_into.rs", "line": 39 }, "group": "clippy::style", @@ -1233,7 +1239,7 @@ { "id": "from_str_radix_10", "id_span": { - "path": "clippy_lints/src/from_str_radix_10.rs", + "path": "src/from_str_radix_10.rs", "line": 37 }, "group": "clippy::style", @@ -1246,7 +1252,7 @@ { "id": "too_many_arguments", "id_span": { - "path": "clippy_lints/src/functions.rs", + "path": "src/functions.rs", "line": 39 }, "group": "clippy::complexity", @@ -1259,7 +1265,7 @@ { "id": "too_many_lines", "id_span": { - "path": "clippy_lints/src/functions.rs", + "path": "src/functions.rs", "line": 62 }, "group": "clippy::pedantic", @@ -1272,7 +1278,7 @@ { "id": "not_unsafe_ptr_arg_deref", "id_span": { - "path": "clippy_lints/src/functions.rs", + "path": "src/functions.rs", "line": 96 }, "group": "clippy::correctness", @@ -1285,7 +1291,7 @@ { "id": "must_use_unit", "id_span": { - "path": "clippy_lints/src/functions.rs", + "path": "src/functions.rs", "line": 117 }, "group": "clippy::style", @@ -1298,7 +1304,7 @@ { "id": "double_must_use", "id_span": { - "path": "clippy_lints/src/functions.rs", + "path": "src/functions.rs", "line": 142 }, "group": "clippy::style", @@ -1311,7 +1317,7 @@ { "id": "must_use_candidate", "id_span": { - "path": "clippy_lints/src/functions.rs", + "path": "src/functions.rs", "line": 170 }, "group": "clippy::pedantic", @@ -1324,7 +1330,7 @@ { "id": "result_unit_err", "id_span": { - "path": "clippy_lints/src/functions.rs", + "path": "src/functions.rs", "line": 216 }, "group": "clippy::style", @@ -1337,7 +1343,7 @@ { "id": "future_not_send", "id_span": { - "path": "clippy_lints/src/future_not_send.rs", + "path": "src/future_not_send.rs", "line": 44 }, "group": "clippy::nursery", @@ -1350,7 +1356,7 @@ { "id": "get_last_with_len", "id_span": { - "path": "clippy_lints/src/get_last_with_len.rs", + "path": "src/get_last_with_len.rs", "line": 40 }, "group": "clippy::complexity", @@ -1363,7 +1369,7 @@ { "id": "identity_op", "id_span": { - "path": "clippy_lints/src/identity_op.rs", + "path": "src/identity_op.rs", "line": 24 }, "group": "clippy::complexity", @@ -1376,7 +1382,7 @@ { "id": "if_let_mutex", "id_span": { - "path": "clippy_lints/src/if_let_mutex.rs", + "path": "src/if_let_mutex.rs", "line": 36 }, "group": "clippy::correctness", @@ -1389,7 +1395,7 @@ { "id": "if_let_some_result", "id_span": { - "path": "clippy_lints/src/if_let_some_result.rs", + "path": "src/if_let_some_result.rs", "line": 34 }, "group": "clippy::style", @@ -1402,7 +1408,7 @@ { "id": "if_not_else", "id_span": { - "path": "clippy_lints/src/if_not_else.rs", + "path": "src/if_not_else.rs", "line": 43 }, "group": "clippy::pedantic", @@ -1415,7 +1421,7 @@ { "id": "implicit_return", "id_span": { - "path": "clippy_lints/src/implicit_return.rs", + "path": "src/implicit_return.rs", "line": 33 }, "group": "clippy::restriction", @@ -1428,7 +1434,7 @@ { "id": "implicit_saturating_sub", "id_span": { - "path": "clippy_lints/src/implicit_saturating_sub.rs", + "path": "src/implicit_saturating_sub.rs", "line": 32 }, "group": "clippy::pedantic", @@ -1441,7 +1447,7 @@ { "id": "inconsistent_struct_constructor", "id_span": { - "path": "clippy_lints/src/inconsistent_struct_constructor.rs", + "path": "src/inconsistent_struct_constructor.rs", "line": 58 }, "group": "clippy::style", @@ -1454,7 +1460,7 @@ { "id": "out_of_bounds_indexing", "id_span": { - "path": "clippy_lints/src/indexing_slicing.rs", + "path": "src/indexing_slicing.rs", "line": 32 }, "group": "clippy::correctness", @@ -1467,7 +1473,7 @@ { "id": "indexing_slicing", "id_span": { - "path": "clippy_lints/src/indexing_slicing.rs", + "path": "src/indexing_slicing.rs", "line": 81 }, "group": "clippy::restriction", @@ -1480,27 +1486,33 @@ { "id": "infinite_iter", "id_span": { - "path": "clippy_lints/src/infinite_iter.rs", + "path": "src/infinite_iter.rs", "line": 21 }, "group": "clippy::correctness", "docs": " **What it does:** Checks for iteration that is guaranteed to be infinite.\n **Why is this bad?** While there may be places where this is acceptable\n (e.g., in event streams), in most cases this is simply an error.\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n use std::iter;\n\n iter::repeat(1_u8).collect::>();\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } }, { "id": "maybe_infinite_iter", "id_span": { - "path": "clippy_lints/src/infinite_iter.rs", + "path": "src/infinite_iter.rs", "line": 40 }, "group": "clippy::pedantic", "docs": " **What it does:** Checks for iteration that may be infinite.\n **Why is this bad?** While there may be places where this is acceptable\n (e.g., in event streams), in most cases this is simply an error.\n\n **Known problems:** The code may have a condition to stop iteration, but\n this lint is not clever enough to analyze it.\n\n **Example:**\n ```rust\n let infinite_iter = 0..;\n [0..].iter().zip(infinite_iter.take_while(|x| *x > 5));\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } }, { "id": "multiple_inherent_impl", "id_span": { - "path": "clippy_lints/src/inherent_impl.rs", + "path": "src/inherent_impl.rs", "line": 37 }, "group": "clippy::restriction", @@ -1513,7 +1525,7 @@ { "id": "inherent_to_string", "id_span": { - "path": "clippy_lints/src/inherent_to_string.rs", + "path": "src/inherent_to_string.rs", "line": 44 }, "group": "clippy::style", @@ -1526,7 +1538,7 @@ { "id": "inherent_to_string_shadow_display", "id_span": { - "path": "clippy_lints/src/inherent_to_string.rs", + "path": "src/inherent_to_string.rs", "line": 89 }, "group": "clippy::correctness", @@ -1539,7 +1551,7 @@ { "id": "inline_fn_without_body", "id_span": { - "path": "clippy_lints/src/inline_fn_without_body.rs", + "path": "src/inline_fn_without_body.rs", "line": 27 }, "group": "clippy::correctness", @@ -1552,7 +1564,7 @@ { "id": "int_plus_one", "id_span": { - "path": "clippy_lints/src/int_plus_one.rs", + "path": "src/int_plus_one.rs", "line": 31 }, "group": "clippy::complexity", @@ -1565,7 +1577,7 @@ { "id": "integer_division", "id_span": { - "path": "clippy_lints/src/integer_division.rs", + "path": "src/integer_division.rs", "line": 26 }, "group": "clippy::restriction", @@ -1578,7 +1590,7 @@ { "id": "items_after_statements", "id_span": { - "path": "clippy_lints/src/items_after_statements.rs", + "path": "src/items_after_statements.rs", "line": 48 }, "group": "clippy::pedantic", @@ -1591,7 +1603,7 @@ { "id": "large_const_arrays", "id_span": { - "path": "clippy_lints/src/large_const_arrays.rs", + "path": "src/large_const_arrays.rs", "line": 30 }, "group": "clippy::perf", @@ -1604,7 +1616,7 @@ { "id": "large_enum_variant", "id_span": { - "path": "clippy_lints/src/large_enum_variant.rs", + "path": "src/large_enum_variant.rs", "line": 39 }, "group": "clippy::perf", @@ -1617,7 +1629,7 @@ { "id": "large_stack_arrays", "id_span": { - "path": "clippy_lints/src/large_stack_arrays.rs", + "path": "src/large_stack_arrays.rs", "line": 23 }, "group": "clippy::pedantic", @@ -1630,7 +1642,7 @@ { "id": "len_zero", "id_span": { - "path": "clippy_lints/src/len_zero.rs", + "path": "src/len_zero.rs", "line": 41 }, "group": "clippy::style", @@ -1643,7 +1655,7 @@ { "id": "len_without_is_empty", "id_span": { - "path": "clippy_lints/src/len_zero.rs", + "path": "src/len_zero.rs", "line": 66 }, "group": "clippy::style", @@ -1656,7 +1668,7 @@ { "id": "comparison_to_empty", "id_span": { - "path": "clippy_lints/src/len_zero.rs", + "path": "src/len_zero.rs", "line": 103 }, "group": "clippy::style", @@ -1669,7 +1681,7 @@ { "id": "useless_let_if_seq", "id_span": { - "path": "clippy_lints/src/let_if_seq.rs", + "path": "src/let_if_seq.rs", "line": 49 }, "group": "clippy::nursery", @@ -1682,7 +1694,7 @@ { "id": "let_underscore_must_use", "id_span": { - "path": "clippy_lints/src/let_underscore.rs", + "path": "src/let_underscore.rs", "line": 29 }, "group": "clippy::restriction", @@ -1695,7 +1707,7 @@ { "id": "let_underscore_lock", "id_span": { - "path": "clippy_lints/src/let_underscore.rs", + "path": "src/let_underscore.rs", "line": 56 }, "group": "clippy::correctness", @@ -1708,7 +1720,7 @@ { "id": "let_underscore_drop", "id_span": { - "path": "clippy_lints/src/let_underscore.rs", + "path": "src/let_underscore.rs", "line": 97 }, "group": "clippy::pedantic", @@ -1721,7 +1733,7 @@ { "id": "needless_lifetimes", "id_span": { - "path": "clippy_lints/src/lifetimes.rs", + "path": "src/lifetimes.rs", "line": 46 }, "group": "clippy::complexity", @@ -1734,7 +1746,7 @@ { "id": "extra_unused_lifetimes", "id_span": { - "path": "clippy_lints/src/lifetimes.rs", + "path": "src/lifetimes.rs", "line": 74 }, "group": "clippy::complexity", @@ -1747,7 +1759,7 @@ { "id": "unreadable_literal", "id_span": { - "path": "clippy_lints/src/literal_representation.rs", + "path": "src/literal_representation.rs", "line": 33 }, "group": "clippy::pedantic", @@ -1760,7 +1772,7 @@ { "id": "mistyped_literal_suffixes", "id_span": { - "path": "clippy_lints/src/literal_representation.rs", + "path": "src/literal_representation.rs", "line": 57 }, "group": "clippy::correctness", @@ -1773,7 +1785,7 @@ { "id": "inconsistent_digit_grouping", "id_span": { - "path": "clippy_lints/src/literal_representation.rs", + "path": "src/literal_representation.rs", "line": 80 }, "group": "clippy::style", @@ -1786,7 +1798,7 @@ { "id": "unusual_byte_groupings", "id_span": { - "path": "clippy_lints/src/literal_representation.rs", + "path": "src/literal_representation.rs", "line": 99 }, "group": "clippy::style", @@ -1799,7 +1811,7 @@ { "id": "large_digit_groups", "id_span": { - "path": "clippy_lints/src/literal_representation.rs", + "path": "src/literal_representation.rs", "line": 118 }, "group": "clippy::pedantic", @@ -1812,7 +1824,7 @@ { "id": "decimal_literal_representation", "id_span": { - "path": "clippy_lints/src/literal_representation.rs", + "path": "src/literal_representation.rs", "line": 136 }, "group": "clippy::restriction", @@ -1825,8 +1837,8 @@ { "id": "manual_memcpy", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 57 + "path": "src/loops/mod.rs", + "line": 50 }, "group": "clippy::perf", "docs": " **What it does:** Checks for for-loops that manually copy items between slices that could be optimized by having a memcpy.\n\n **Why is this bad?** It is not as fast as a memcpy.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let src = vec![1];\n # let mut dst = vec![0; 65];\n for i in 0..src.len() {\n dst[i + 64] = src[i];\n }\n ```\n Could be written as:\n ```rust\n # let src = vec![1];\n # let mut dst = vec![0; 65];\n dst[64..(src.len() + 64)].clone_from_slice(&src[..]);\n ```\n", @@ -1838,8 +1850,8 @@ { "id": "needless_range_loop", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 85 + "path": "src/loops/mod.rs", + "line": 78 }, "group": "clippy::style", "docs": " **What it does:** Checks for looping over the range of `0..len` of some collection just to get the values by index.\n\n **Why is this bad?** Just iterating the collection itself makes the intent\n more clear and is probably faster.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let vec = vec!['a', 'b', 'c'];\n for i in 0..vec.len() {\n println!(\"{}\", vec[i]);\n }\n ```\n Could be written as:\n ```rust\n let vec = vec!['a', 'b', 'c'];\n for i in vec {\n println!(\"{}\", i);\n }\n ```\n", @@ -1851,8 +1863,8 @@ { "id": "explicit_iter_loop", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 114 + "path": "src/loops/mod.rs", + "line": 107 }, "group": "clippy::pedantic", "docs": " **What it does:** Checks for loops on `x.iter()` where `&x` will do, and suggests the latter.\n\n **Why is this bad?** Readability.\n\n **Known problems:** False negatives. We currently only warn on some known\n types.\n\n **Example:**\n ```rust\n // with `y` a `Vec` or slice:\n # let y = vec![1];\n for x in y.iter() {\n // ..\n }\n ```\n can be rewritten to\n ```rust\n # let y = vec![1];\n for x in &y {\n // ..\n }\n ```\n", @@ -1864,8 +1876,8 @@ { "id": "explicit_into_iter_loop", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 142 + "path": "src/loops/mod.rs", + "line": 135 }, "group": "clippy::pedantic", "docs": " **What it does:** Checks for loops on `y.into_iter()` where `y` will do, and suggests the latter.\n\n **Why is this bad?** Readability.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n # let y = vec![1];\n // with `y` a `Vec` or slice:\n for x in y.into_iter() {\n // ..\n }\n ```\n can be rewritten to\n ```rust\n # let y = vec![1];\n for x in y {\n // ..\n }\n ```\n", @@ -1877,8 +1889,8 @@ { "id": "iter_next_loop", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 165 + "path": "src/loops/mod.rs", + "line": 158 }, "group": "clippy::correctness", "docs": " **What it does:** Checks for loops on `x.next()`.\n **Why is this bad?** `next()` returns either `Some(value)` if there was a\n value, or `None` otherwise. The insidious thing is that `Option<_>`\n implements `IntoIterator`, so that possibly one value will be iterated,\n leading to some hard to find bugs. No one will want to write such code\n [except to win an Underhanded Rust\n Contest](https://www.reddit.com/r/rust/comments/3hb0wm/underhanded_rust_contest/cu5yuhr).\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n for x in y.next() {\n ..\n }\n ```\n", @@ -1890,8 +1902,8 @@ { "id": "for_loops_over_fallibles", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 208 + "path": "src/loops/mod.rs", + "line": 201 }, "group": "clippy::correctness", "docs": " **What it does:** Checks for `for` loops over `Option` or `Result` values.\n **Why is this bad?** Readability. This is more clearly expressed as an `if\n let`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let opt = Some(1);\n\n // Bad\n for x in opt {\n // ..\n }\n\n // Good\n if let Some(x) = opt {\n // ..\n }\n ```\n\n // or\n\n ```rust\n # let res: Result = Ok(1);\n\n // Bad\n for x in &res {\n // ..\n }\n\n // Good\n if let Ok(x) = res {\n // ..\n }\n ```\n", @@ -1903,8 +1915,8 @@ { "id": "while_let_loop", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 237 + "path": "src/loops/mod.rs", + "line": 230 }, "group": "clippy::complexity", "docs": " **What it does:** Detects `loop + match` combinations that are easier written as a `while let` loop.\n\n **Why is this bad?** The `while let` loop is usually shorter and more\n readable.\n\n **Known problems:** Sometimes the wrong binding is displayed ([#383](https://github.com/rust-lang/rust-clippy/issues/383)).\n\n **Example:**\n ```rust,no_run\n # let y = Some(1);\n loop {\n let x = match y {\n Some(x) => x,\n None => break,\n };\n // .. do something with x\n }\n // is easier written as\n while let Some(x) = y {\n // .. do something with x\n };\n ```\n", @@ -1916,8 +1928,8 @@ { "id": "needless_collect", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 259 + "path": "src/loops/mod.rs", + "line": 252 }, "group": "clippy::perf", "docs": " **What it does:** Checks for functions collecting an iterator when collect is not needed.\n\n **Why is this bad?** `collect` causes the allocation of a new data structure,\n when this allocation may not be needed.\n\n **Known problems:**\n None\n\n **Example:**\n ```rust\n # let iterator = vec![1].into_iter();\n let len = iterator.clone().collect::>().len();\n // should be\n let len = iterator.count();\n ```\n", @@ -1929,8 +1941,8 @@ { "id": "explicit_counter_loop", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 289 + "path": "src/loops/mod.rs", + "line": 282 }, "group": "clippy::complexity", "docs": " **What it does:** Checks `for` loops over slices with an explicit counter and suggests the use of `.enumerate()`.\n\n **Why is it bad?** Using `.enumerate()` makes the intent more clear,\n declutters the code and may be faster in some instances.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let v = vec![1];\n # fn bar(bar: usize, baz: usize) {}\n let mut i = 0;\n for item in &v {\n bar(i, *item);\n i += 1;\n }\n ```\n Could be written as\n ```rust\n # let v = vec![1];\n # fn bar(bar: usize, baz: usize) {}\n for (i, item) in v.iter().enumerate() { bar(i, *item); }\n ```\n", @@ -1942,8 +1954,8 @@ { "id": "empty_loop", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 322 + "path": "src/loops/mod.rs", + "line": 315 }, "group": "clippy::style", "docs": " **What it does:** Checks for empty `loop` expressions.\n **Why is this bad?** These busy loops burn CPU cycles without doing\n anything. It is _almost always_ a better idea to `panic!` than to have\n a busy loop.\n\n If panicking isn't possible, think of the environment and either:\n - block on something\n - sleep the thread for some microseconds\n - yield or pause the thread\n\n For `std` targets, this can be done with\n [`std::thread::sleep`](https://doc.rust-lang.org/std/thread/fn.sleep.html)\n or [`std::thread::yield_now`](https://doc.rust-lang.org/std/thread/fn.yield_now.html).\n\n For `no_std` targets, doing this is more complicated, especially because\n `#[panic_handler]`s can't panic. To stop/pause the thread, you will\n probably need to invoke some target-specific intrinsic. Examples include:\n - [`x86_64::instructions::hlt`](https://docs.rs/x86_64/0.12.2/x86_64/instructions/fn.hlt.html)\n - [`cortex_m::asm::wfi`](https://docs.rs/cortex-m/0.6.3/cortex_m/asm/fn.wfi.html)\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n loop {}\n ```\n", @@ -1955,8 +1967,8 @@ { "id": "while_let_on_iterator", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 341 + "path": "src/loops/mod.rs", + "line": 334 }, "group": "clippy::style", "docs": " **What it does:** Checks for `while let` expressions on iterators.\n **Why is this bad?** Readability. A simple `for` loop is shorter and conveys\n the intent better.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n while let Some(val) = iter() {\n ..\n }\n ```\n", @@ -1968,8 +1980,8 @@ { "id": "for_kv_map", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 369 + "path": "src/loops/mod.rs", + "line": 362 }, "group": "clippy::style", "docs": " **What it does:** Checks for iterating a map (`HashMap` or `BTreeMap`) and ignoring either the keys or values.\n\n **Why is this bad?** Readability. There are `keys` and `values` methods that\n can be used to express that don't need the values or keys.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n for (k, _) in &map {\n ..\n }\n ```\n\n could be replaced by\n\n ```ignore\n for k in map.keys() {\n ..\n }\n ```\n", @@ -1981,8 +1993,8 @@ { "id": "never_loop", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 390 + "path": "src/loops/mod.rs", + "line": 383 }, "group": "clippy::correctness", "docs": " **What it does:** Checks for loops that will always `break`, `return` or `continue` an outer loop.\n\n **Why is this bad?** This loop never loops, all it does is obfuscating the\n code.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n loop {\n ..;\n break;\n }\n ```\n", @@ -1994,8 +2006,8 @@ { "id": "mut_range_bound", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 410 + "path": "src/loops/mod.rs", + "line": 403 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for loops which have a range bound that is a mutable variable\n **Why is this bad?** One might think that modifying the mutable variable changes the loop bounds\n\n **Known problems:** None\n\n **Example:**\n ```rust\n let mut foo = 42;\n for i in 0..foo {\n foo -= 1;\n println!(\"{}\", i); // prints numbers from 0 to 42, not 0 to 21\n }\n ```\n", @@ -2007,8 +2019,8 @@ { "id": "while_immutable_condition", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 433 + "path": "src/loops/mod.rs", + "line": 426 }, "group": "clippy::correctness", "docs": " **What it does:** Checks whether variables used within while loop condition can be (and are) mutated in the body.\n\n **Why is this bad?** If the condition is unchanged, entering the body of the loop\n will lead to an infinite loop.\n\n **Known problems:** If the `while`-loop is in a closure, the check for mutation of the\n condition variables in the body can cause false negatives. For example when only `Upvar` `a` is\n in the condition and only `Upvar` `b` gets mutated in the body, the lint will not trigger.\n\n **Example:**\n ```rust\n let i = 0;\n while i > 10 {\n println!(\"let me loop forever!\");\n }\n ```\n", @@ -2020,8 +2032,8 @@ { "id": "same_item_push", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 466 + "path": "src/loops/mod.rs", + "line": 459 }, "group": "clippy::style", "docs": " **What it does:** Checks whether a for loop is being used to push a constant value into a Vec.\n\n **Why is this bad?** This kind of operation can be expressed more succinctly with\n `vec![item;SIZE]` or `vec.resize(NEW_SIZE, item)` and using these alternatives may also\n have better performance.\n **Known problems:** None\n\n **Example:**\n ```rust\n let item1 = 2;\n let item2 = 3;\n let mut vec: Vec = Vec::new();\n for _ in 0..20 {\n vec.push(item1);\n }\n for _ in 0..30 {\n vec.push(item2);\n }\n ```\n could be written as\n ```rust\n let item1 = 2;\n let item2 = 3;\n let mut vec: Vec = vec![item1; 20];\n vec.resize(20 + 30, item2);\n ```\n", @@ -2033,8 +2045,8 @@ { "id": "single_element_loop", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 491 + "path": "src/loops/mod.rs", + "line": 484 }, "group": "clippy::complexity", "docs": " **What it does:** Checks whether a for loop has a single element.\n **Why is this bad?** There is no reason to have a loop of a\n single element.\n **Known problems:** None\n\n **Example:**\n ```rust\n let item1 = 2;\n for item in &[item1] {\n println!(\"{}\", item);\n }\n ```\n could be written as\n ```rust\n let item1 = 2;\n let item = &item1;\n println!(\"{}\", item);\n ```\n", @@ -2046,8 +2058,8 @@ { "id": "manual_flatten", "id_span": { - "path": "clippy_lints/src/loops.rs", - "line": 522 + "path": "src/loops/mod.rs", + "line": 515 }, "group": "clippy::complexity", "docs": " **What it does:** Check for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the iterator element is used.\n\n **Why is this bad?** It is verbose and can be simplified\n by first calling the `flatten` method on the `Iterator`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let x = vec![Some(1), Some(2), Some(3)];\n for n in x {\n if let Some(n) = n {\n println!(\"{}\", n);\n }\n }\n ```\n Use instead:\n ```rust\n let x = vec![Some(1), Some(2), Some(3)];\n for n in x.into_iter().flatten() {\n println!(\"{}\", n);\n }\n ```\n", @@ -2059,7 +2071,7 @@ { "id": "macro_use_imports", "id_span": { - "path": "clippy_lints/src/macro_use.rs", + "path": "src/macro_use.rs", "line": 25 }, "group": "clippy::pedantic", @@ -2072,7 +2084,7 @@ { "id": "main_recursion", "id_span": { - "path": "clippy_lints/src/main_recursion.rs", + "path": "src/main_recursion.rs", "line": 22 }, "group": "clippy::style", @@ -2085,7 +2097,7 @@ { "id": "manual_async_fn", "id_span": { - "path": "clippy_lints/src/manual_async_fn.rs", + "path": "src/manual_async_fn.rs", "line": 32 }, "group": "clippy::style", @@ -2098,11 +2110,11 @@ { "id": "manual_map", "id_span": { - "path": "clippy_lints/src/manual_map.rs", - "line": 36 + "path": "src/manual_map.rs", + "line": 44 }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for usages of `match` which could be implemented using `map`\n **Why is this bad?** Using the `map` method is clearer and more concise.\n\n **Known problems:** `map` is not capable of representing some control flow which works fine in `match`.\n\n **Example:**\n\n ```rust\n match Some(0) {\n Some(x) => Some(x + 1),\n None => None,\n };\n ```\n Use instead:\n ```rust\n Some(0).map(|x| x + 1);\n ```\n", + "group": "clippy::style", + "docs": " **What it does:** Checks for usages of `match` which could be implemented using `map`\n **Why is this bad?** Using the `map` method is clearer and more concise.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n match Some(0) {\n Some(x) => Some(x + 1),\n None => None,\n };\n ```\n Use instead:\n ```rust\n Some(0).map(|x| x + 1);\n ```\n", "applicability": { "is_multi_suggestion": false, "applicability": "MachineApplicable" @@ -2111,7 +2123,7 @@ { "id": "manual_non_exhaustive", "id_span": { - "path": "clippy_lints/src/manual_non_exhaustive.rs", + "path": "src/manual_non_exhaustive.rs", "line": 56 }, "group": "clippy::style", @@ -2124,7 +2136,7 @@ { "id": "manual_ok_or", "id_span": { - "path": "clippy_lints/src/manual_ok_or.rs", + "path": "src/manual_ok_or.rs", "line": 34 }, "group": "clippy::pedantic", @@ -2137,7 +2149,7 @@ { "id": "manual_strip", "id_span": { - "path": "clippy_lints/src/manual_strip.rs", + "path": "src/manual_strip.rs", "line": 52 }, "group": "clippy::complexity", @@ -2150,7 +2162,7 @@ { "id": "manual_unwrap_or", "id_span": { - "path": "clippy_lints/src/manual_unwrap_or.rs", + "path": "src/manual_unwrap_or.rs", "line": 36 }, "group": "clippy::complexity", @@ -2163,7 +2175,7 @@ { "id": "map_clone", "id_span": { - "path": "clippy_lints/src/map_clone.rs", + "path": "src/map_clone.rs", "line": 40 }, "group": "clippy::style", @@ -2176,7 +2188,7 @@ { "id": "map_err_ignore", "id_span": { - "path": "clippy_lints/src/map_err_ignore.rs", + "path": "src/map_err_ignore.rs", "line": 101 }, "group": "clippy::restriction", @@ -2189,7 +2201,7 @@ { "id": "map_identity", "id_span": { - "path": "clippy_lints/src/map_identity.rs", + "path": "src/map_identity.rs", "line": 30 }, "group": "clippy::complexity", @@ -2202,27 +2214,33 @@ { "id": "option_map_unit_fn", "id_span": { - "path": "clippy_lints/src/map_unit_fn.rs", + "path": "src/map_unit_fn.rs", "line": 48 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for usage of `option.map(f)` where f is a function or closure that returns the unit type `()`.\n\n **Why is this bad?** Readability, this can be written more clearly with\n an if let statement\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # fn do_stuff() -> Option { Some(String::new()) }\n # fn log_err_msg(foo: String) -> Option { Some(foo) }\n # fn format_msg(foo: String) -> String { String::new() }\n let x: Option = do_stuff();\n x.map(log_err_msg);\n # let x: Option = do_stuff();\n x.map(|msg| log_err_msg(format_msg(msg)));\n ```\n\n The correct use would be:\n\n ```rust\n # fn do_stuff() -> Option { Some(String::new()) }\n # fn log_err_msg(foo: String) -> Option { Some(foo) }\n # fn format_msg(foo: String) -> String { String::new() }\n let x: Option = do_stuff();\n if let Some(msg) = x {\n log_err_msg(msg);\n }\n\n # let x: Option = do_stuff();\n if let Some(msg) = x {\n log_err_msg(format_msg(msg));\n }\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": true, + "applicability": "MachineApplicable" + } }, { "id": "result_map_unit_fn", "id_span": { - "path": "clippy_lints/src/map_unit_fn.rs", + "path": "src/map_unit_fn.rs", "line": 89 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for usage of `result.map(f)` where f is a function or closure that returns the unit type `()`.\n\n **Why is this bad?** Readability, this can be written more clearly with\n an if let statement\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # fn do_stuff() -> Result { Ok(String::new()) }\n # fn log_err_msg(foo: String) -> Result { Ok(foo) }\n # fn format_msg(foo: String) -> String { String::new() }\n let x: Result = do_stuff();\n x.map(log_err_msg);\n # let x: Result = do_stuff();\n x.map(|msg| log_err_msg(format_msg(msg)));\n ```\n\n The correct use would be:\n\n ```rust\n # fn do_stuff() -> Result { Ok(String::new()) }\n # fn log_err_msg(foo: String) -> Result { Ok(foo) }\n # fn format_msg(foo: String) -> String { String::new() }\n let x: Result = do_stuff();\n if let Ok(msg) = x {\n log_err_msg(msg);\n };\n # let x: Result = do_stuff();\n if let Ok(msg) = x {\n log_err_msg(format_msg(msg));\n };\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": true, + "applicability": "MachineApplicable" + } }, { "id": "match_on_vec_items", "id_span": { - "path": "clippy_lints/src/match_on_vec_items.rs", + "path": "src/match_on_vec_items.rs", "line": 41 }, "group": "clippy::pedantic", @@ -2235,27 +2253,33 @@ { "id": "single_match", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 55 }, "group": "clippy::style", "docs": " **What it does:** Checks for matches with a single arm where an `if let` will usually suffice.\n\n **Why is this bad?** Just readability – `if let` nests less than a `match`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn bar(stool: &str) {}\n # let x = Some(\"abc\");\n // Bad\n match x {\n Some(ref foo) => bar(foo),\n _ => (),\n }\n\n // Good\n if let Some(ref foo) = x {\n bar(foo);\n }\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": "HasPlaceholders" + } }, { "id": "single_match_else", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 94 }, "group": "clippy::pedantic", "docs": " **What it does:** Checks for matches with two arms where an `if let else` will usually suffice.\n\n **Why is this bad?** Just readability – `if let` nests less than a `match`.\n\n **Known problems:** Personal style preferences may differ.\n\n **Example:**\n\n Using `match`:\n\n ```rust\n # fn bar(foo: &usize) {}\n # let other_ref: usize = 1;\n # let x: Option<&usize> = Some(&1);\n match x {\n Some(ref foo) => bar(foo),\n _ => bar(&other_ref),\n }\n ```\n\n Using `if let` with `else`:\n\n ```rust\n # fn bar(foo: &usize) {}\n # let other_ref: usize = 1;\n # let x: Option<&usize> = Some(&1);\n if let Some(ref foo) = x {\n bar(foo);\n } else {\n bar(&other_ref);\n }\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": "HasPlaceholders" + } }, { "id": "match_ref_pats", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 125 }, "group": "clippy::style", @@ -2268,7 +2292,7 @@ { "id": "match_bool", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 159 }, "group": "clippy::pedantic", @@ -2281,7 +2305,7 @@ { "id": "match_overlapping_arm", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 181 }, "group": "clippy::style", @@ -2294,7 +2318,7 @@ { "id": "match_wild_err_arm", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 203 }, "group": "clippy::pedantic", @@ -2307,7 +2331,7 @@ { "id": "match_as_ref", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 229 }, "group": "clippy::complexity", @@ -2320,7 +2344,7 @@ { "id": "wildcard_enum_match_arm", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 258 }, "group": "clippy::restriction", @@ -2333,7 +2357,7 @@ { "id": "match_wildcard_for_single_variants", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 290 }, "group": "clippy::pedantic", @@ -2346,7 +2370,7 @@ { "id": "wildcard_in_or_patterns", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 317 }, "group": "clippy::complexity", @@ -2359,7 +2383,7 @@ { "id": "infallible_destructuring_match", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 352 }, "group": "clippy::style", @@ -2372,7 +2396,7 @@ { "id": "match_single_binding", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 380 }, "group": "clippy::complexity", @@ -2385,7 +2409,7 @@ { "id": "rest_pat_in_fully_bound_structs", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 410 }, "group": "clippy::restriction", @@ -2398,7 +2422,7 @@ { "id": "redundant_pattern_matching", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 458 }, "group": "clippy::style", @@ -2411,7 +2435,7 @@ { "id": "match_like_matches_macro", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 491 }, "group": "clippy::style", @@ -2424,7 +2448,7 @@ { "id": "match_same_arms", "id_span": { - "path": "clippy_lints/src/matches.rs", + "path": "src/matches.rs", "line": 532 }, "group": "clippy::pedantic", @@ -2437,7 +2461,7 @@ { "id": "mem_discriminant_non_enum", "id_span": { - "path": "clippy_lints/src/mem_discriminant.rs", + "path": "src/mem_discriminant.rs", "line": 25 }, "group": "clippy::correctness", @@ -2450,7 +2474,7 @@ { "id": "mem_forget", "id_span": { - "path": "clippy_lints/src/mem_forget.rs", + "path": "src/mem_forget.rs", "line": 21 }, "group": "clippy::restriction", @@ -2463,7 +2487,7 @@ { "id": "mem_replace_option_with_none", "id_span": { - "path": "clippy_lints/src/mem_replace.rs", + "path": "src/mem_replace.rs", "line": 37 }, "group": "clippy::style", @@ -2476,7 +2500,7 @@ { "id": "mem_replace_with_uninit", "id_span": { - "path": "clippy_lints/src/mem_replace.rs", + "path": "src/mem_replace.rs", "line": 69 }, "group": "clippy::correctness", @@ -2489,7 +2513,7 @@ { "id": "mem_replace_with_default", "id_span": { - "path": "clippy_lints/src/mem_replace.rs", + "path": "src/mem_replace.rs", "line": 93 }, "group": "clippy::style", @@ -2502,8 +2526,8 @@ { "id": "unwrap_used", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 82 + "path": "src/methods/mod.rs", + "line": 84 }, "group": "clippy::restriction", "docs": " **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s.\n **Why is this bad?** It is better to handle the `None` or `Err` case,\n or at least call `.expect(_)` with a more helpful message. Still, for a lot of\n quick-and-dirty code, `unwrap` is a good choice, which is why this lint is\n `Allow` by default.\n\n `result.unwrap()` will let the thread panic on `Err` values.\n Normally, you want to implement more sophisticated error handling,\n and propagate errors upwards with `?` operator.\n\n Even if you want to panic on errors, not all `Error`s implement good\n messages on display. Therefore, it may be beneficial to look at the places\n where they may get displayed. Activate this lint to do just that.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n # let opt = Some(1);\n\n // Bad\n opt.unwrap();\n\n // Good\n opt.expect(\"more helpful message\");\n ```\n\n // or\n\n ```rust\n # let res: Result = Ok(1);\n\n // Bad\n res.unwrap();\n\n // Good\n res.expect(\"more helpful message\");\n ```\n", @@ -2512,8 +2536,8 @@ { "id": "expect_used", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 124 + "path": "src/methods/mod.rs", + "line": 126 }, "group": "clippy::restriction", "docs": " **What it does:** Checks for `.expect()` calls on `Option`s and `Result`s.\n **Why is this bad?** Usually it is better to handle the `None` or `Err` case.\n Still, for a lot of quick-and-dirty code, `expect` is a good choice, which is why\n this lint is `Allow` by default.\n\n `result.expect()` will let the thread panic on `Err`\n values. Normally, you want to implement more sophisticated error handling,\n and propagate errors upwards with `?` operator.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust,ignore\n # let opt = Some(1);\n\n // Bad\n opt.expect(\"one\");\n\n // Good\n let opt = Some(1);\n opt?;\n ```\n\n // or\n\n ```rust\n # let res: Result = Ok(1);\n\n // Bad\n res.expect(\"one\");\n\n // Good\n res?;\n # Ok::<(), ()>(())\n ```\n", @@ -2522,8 +2546,8 @@ { "id": "should_implement_trait", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 153 + "path": "src/methods/mod.rs", + "line": 155 }, "group": "clippy::style", "docs": " **What it does:** Checks for methods that should live in a trait implementation of a `std` trait (see [llogiq's blog\n post](http://llogiq.github.io/2015/07/30/traits.html) for further\n information) instead of an inherent implementation.\n\n **Why is this bad?** Implementing the traits improve ergonomics for users of\n the code, often with very little cost. Also people seeing a `mul(...)`\n method\n may expect `*` to work equally, so you should have good reason to disappoint\n them.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n struct X;\n impl X {\n fn add(&self, other: &X) -> X {\n // ..\n # X\n }\n }\n ```\n", @@ -2535,28 +2559,34 @@ { "id": "wrong_self_convention", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 186 + "path": "src/methods/mod.rs", + "line": 188 }, "group": "clippy::style", "docs": " **What it does:** Checks for methods with certain name prefixes and which doesn't match how self is taken. The actual rules are:\n\n |Prefix |`self` taken |\n |-------|----------------------|\n |`as_` |`&self` or `&mut self`|\n |`from_`| none |\n |`into_`|`self` |\n |`is_` |`&self` or none |\n |`to_` |`&self` |\n\n **Why is this bad?** Consistency breeds readability. If you follow the\n conventions, your users won't be surprised that they, e.g., need to supply a\n mutable reference to a `as_..` function.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct X;\n impl X {\n fn as_str(self) -> &'static str {\n // ..\n # \"\"\n }\n }\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } }, { "id": "wrong_pub_self_convention", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 210 + "path": "src/methods/mod.rs", + "line": 212 }, "group": "clippy::restriction", "docs": " **What it does:** This is the same as [`wrong_self_convention`](#wrong_self_convention), but for public items.\n\n **Why is this bad?** See [`wrong_self_convention`](#wrong_self_convention).\n\n **Known problems:** Actually *renaming* the function may break clients if\n the function is part of the public interface. In that case, be mindful of\n the stability guarantees you've given your users.\n\n **Example:**\n ```rust\n # struct X;\n impl<'a> X {\n pub fn as_str(self) -> &'a str {\n \"foo\"\n }\n }\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } }, { "id": "ok_expect", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 233 + "path": "src/methods/mod.rs", + "line": 235 }, "group": "clippy::style", "docs": " **What it does:** Checks for usage of `ok().expect(..)`.\n **Why is this bad?** Because you usually call `expect()` on the `Result`\n directly to get a better error message.\n\n **Known problems:** The error type needs to implement `Debug`\n\n **Example:**\n ```rust\n # let x = Ok::<_, ()>(());\n\n // Bad\n x.ok().expect(\"why did I do this again?\");\n\n // Good\n x.expect(\"why did I do this again?\");\n ```\n", @@ -2568,8 +2598,8 @@ { "id": "map_unwrap_or", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 270 + "path": "src/methods/mod.rs", + "line": 272 }, "group": "clippy::pedantic", "docs": " **What it does:** Checks for usage of `option.map(_).unwrap_or(_)` or `option.map(_).unwrap_or_else(_)` or `result.map(_).unwrap_or_else(_)`.\n\n **Why is this bad?** Readability, these can be written more concisely (resp.) as\n `option.map_or(_, _)`, `option.map_or_else(_, _)` and `result.map_or_else(_, _)`.\n\n **Known problems:** The order of the arguments is not in execution order\n\n **Examples:**\n ```rust\n # let x = Some(1);\n\n // Bad\n x.map(|a| a + 1).unwrap_or(0);\n\n // Good\n x.map_or(0, |a| a + 1);\n ```\n\n // or\n\n ```rust\n # let x: Result = Ok(1);\n # fn some_function(foo: ()) -> usize { 1 }\n\n // Bad\n x.map(|a| a + 1).unwrap_or_else(some_function);\n\n // Good\n x.map_or_else(some_function, |a| a + 1);\n ```\n", @@ -2581,28 +2611,34 @@ { "id": "option_map_or_none", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 293 + "path": "src/methods/mod.rs", + "line": 295 }, "group": "clippy::style", "docs": " **What it does:** Checks for usage of `_.map_or(None, _)`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.and_then(_)`.\n\n **Known problems:** The order of the arguments is not in execution order.\n\n **Example:**\n ```rust\n # let opt = Some(1);\n\n // Bad\n opt.map_or(None, |a| Some(a + 1));\n\n // Good\n opt.and_then(|a| Some(a + 1));\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } }, { "id": "result_map_or_into_option", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 319 + "path": "src/methods/mod.rs", + "line": 321 }, "group": "clippy::style", "docs": " **What it does:** Checks for usage of `_.map_or(None, Some)`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.ok()`.\n\n **Known problems:** None.\n\n **Example:**\n\n Bad:\n ```rust\n # let r: Result = Ok(1);\n assert_eq!(Some(1), r.map_or(None, Some));\n ```\n\n Good:\n ```rust\n # let r: Result = Ok(1);\n assert_eq!(Some(1), r.ok());\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } }, { "id": "bind_instead_of_map", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 352 + "path": "src/methods/mod.rs", + "line": 354 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or `_.or_else(|x| Err(y))`.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.map(|x| y)` or `_.map_err(|x| y)`.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n # fn opt() -> Option<&'static str> { Some(\"42\") }\n # fn res() -> Result<&'static str, &'static str> { Ok(\"42\") }\n let _ = opt().and_then(|s| Some(s.len()));\n let _ = res().and_then(|s| if s.len() == 42 { Ok(10) } else { Ok(20) });\n let _ = res().or_else(|s| if s.len() == 42 { Err(10) } else { Err(20) });\n ```\n\n The correct use would be:\n\n ```rust\n # fn opt() -> Option<&'static str> { Some(\"42\") }\n # fn res() -> Result<&'static str, &'static str> { Ok(\"42\") }\n let _ = opt().map(|s| s.len());\n let _ = res().map(|s| if s.len() == 42 { 10 } else { 20 });\n let _ = res().map_err(|s| if s.len() == 42 { 10 } else { 20 });\n ```\n", @@ -2614,8 +2650,8 @@ { "id": "filter_next", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 375 + "path": "src/methods/mod.rs", + "line": 377 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for usage of `_.filter(_).next()`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.find(_)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let vec = vec![1];\n vec.iter().filter(|x| **x == 0).next();\n ```\n Could be written as\n ```rust\n # let vec = vec![1];\n vec.iter().find(|x| **x == 0);\n ```\n", @@ -2627,8 +2663,8 @@ { "id": "skip_while_next", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 398 + "path": "src/methods/mod.rs", + "line": 400 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for usage of `_.skip_while(condition).next()`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.find(!condition)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let vec = vec![1];\n vec.iter().skip_while(|x| **x == 0).next();\n ```\n Could be written as\n ```rust\n # let vec = vec![1];\n vec.iter().find(|x| **x != 0);\n ```\n", @@ -2640,8 +2676,8 @@ { "id": "map_flatten", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 421 + "path": "src/methods/mod.rs", + "line": 423 }, "group": "clippy::pedantic", "docs": " **What it does:** Checks for usage of `_.map(_).flatten(_)`,\n **Why is this bad?** Readability, this can be written more concisely as\n `_.flat_map(_)`\n\n **Known problems:**\n\n **Example:**\n ```rust\n let vec = vec![vec![1]];\n\n // Bad\n vec.iter().map(|x| x.iter()).flatten();\n\n // Good\n vec.iter().flat_map(|x| x.iter());\n ```\n", @@ -2653,8 +2689,8 @@ { "id": "filter_map", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 450 + "path": "src/methods/mod.rs", + "line": 452 }, "group": "clippy::pedantic", "docs": " **What it does:** Checks for usage of `_.filter(_).map(_)`, `_.filter(_).flat_map(_)`, `_.filter_map(_).flat_map(_)` and similar.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.filter_map(_)`.\n\n **Known problems:** Often requires a condition + Option/Iterator creation\n inside the closure.\n\n **Example:**\n ```rust\n let vec = vec![1];\n\n // Bad\n vec.iter().filter(|x| **x == 0).map(|x| *x * 2);\n\n // Good\n vec.iter().filter_map(|x| if *x == 0 {\n Some(*x * 2)\n } else {\n None\n });\n ```\n", @@ -2666,28 +2702,34 @@ { "id": "manual_filter_map", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 476 + "path": "src/methods/mod.rs", + "line": 478 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for usage of `_.filter(_).map(_)` that can be written more simply as `filter_map(_)`.\n\n **Why is this bad?** Redundant code in the `filter` and `map` operations is poor style and\n less performant.\n\n **Known problems:** None.\n\n **Example:**\n Bad:\n ```rust\n (0_i32..10)\n .filter(|n| n.checked_add(1).is_some())\n .map(|n| n.checked_add(1).unwrap());\n ```\n\n Good:\n ```rust\n (0_i32..10).filter_map(|n| n.checked_add(1));\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } }, { "id": "manual_find_map", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 502 + "path": "src/methods/mod.rs", + "line": 504 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for usage of `_.find(_).map(_)` that can be written more simply as `find_map(_)`.\n\n **Why is this bad?** Redundant code in the `find` and `map` operations is poor style and\n less performant.\n\n **Known problems:** None.\n\n **Example:**\n Bad:\n ```rust\n (0_i32..10)\n .find(|n| n.checked_add(1).is_some())\n .map(|n| n.checked_add(1).unwrap());\n ```\n\n Good:\n ```rust\n (0_i32..10).find_map(|n| n.checked_add(1));\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } }, { "id": "filter_map_next", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 524 + "path": "src/methods/mod.rs", + "line": 526 }, "group": "clippy::pedantic", "docs": " **What it does:** Checks for usage of `_.filter_map(_).next()`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.find_map(_)`.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n (0..3).filter_map(|x| if x == 2 { Some(x) } else { None }).next();\n ```\n Can be written as\n\n ```rust\n (0..3).find_map(|x| if x == 2 { Some(x) } else { None });\n ```\n", @@ -2699,8 +2741,8 @@ { "id": "flat_map_identity", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 546 + "path": "src/methods/mod.rs", + "line": 548 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for usage of `flat_map(|x| x)`.\n **Why is this bad?** Readability, this can be written more concisely by using `flatten`.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n # let iter = vec![vec![0]].into_iter();\n iter.flat_map(|x| x);\n ```\n Can be written as\n ```rust\n # let iter = vec![vec![0]].into_iter();\n iter.flatten();\n ```\n", @@ -2712,8 +2754,8 @@ { "id": "search_is_some", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 570 + "path": "src/methods/mod.rs", + "line": 572 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for an iterator or string search (such as `find()`, `position()`, or `rposition()`) followed by a call to `is_some()`.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.any(_)` or `_.contains(_)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let vec = vec![1];\n vec.iter().find(|x| **x == 0).is_some();\n ```\n Could be written as\n ```rust\n # let vec = vec![1];\n vec.iter().any(|x| *x == 0);\n ```\n", @@ -2725,8 +2767,8 @@ { "id": "chars_next_cmp", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 594 + "path": "src/methods/mod.rs", + "line": 596 }, "group": "clippy::style", "docs": " **What it does:** Checks for usage of `.chars().next()` on a `str` to check if it starts with a given char.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.starts_with(_)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let name = \"foo\";\n if name.chars().next() == Some('_') {};\n ```\n Could be written as\n ```rust\n let name = \"foo\";\n if name.starts_with('_') {};\n ```\n", @@ -2735,8 +2777,8 @@ { "id": "or_fun_call", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 625 + "path": "src/methods/mod.rs", + "line": 627 }, "group": "clippy::perf", "docs": " **What it does:** Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`, etc., and suggests to use `or_else`, `unwrap_or_else`, etc., or\n `unwrap_or_default` instead.\n\n **Why is this bad?** The function will always be called and potentially\n allocate an object acting as the default.\n\n **Known problems:** If the function has side-effects, not calling it will\n change the semantic of the program, but you shouldn't rely on that anyway.\n\n **Example:**\n ```rust\n # let foo = Some(String::new());\n foo.unwrap_or(String::new());\n ```\n this can instead be written:\n ```rust\n # let foo = Some(String::new());\n foo.unwrap_or_else(String::new);\n ```\n or\n ```rust\n # let foo = Some(String::new());\n foo.unwrap_or_default();\n ```\n", @@ -2748,8 +2790,8 @@ { "id": "expect_fun_call", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 660 + "path": "src/methods/mod.rs", + "line": 662 }, "group": "clippy::perf", "docs": " **What it does:** Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`, etc., and suggests to use `unwrap_or_else` instead\n\n **Why is this bad?** The function will always be called.\n\n **Known problems:** If the function has side-effects, not calling it will\n change the semantics of the program, but you shouldn't rely on that anyway.\n\n **Example:**\n ```rust\n # let foo = Some(String::new());\n # let err_code = \"418\";\n # let err_msg = \"I'm a teapot\";\n foo.expect(&format!(\"Err {}: {}\", err_code, err_msg));\n ```\n or\n ```rust\n # let foo = Some(String::new());\n # let err_code = \"418\";\n # let err_msg = \"I'm a teapot\";\n foo.expect(format!(\"Err {}: {}\", err_code, err_msg).as_str());\n ```\n this can instead be written:\n ```rust\n # let foo = Some(String::new());\n # let err_code = \"418\";\n # let err_msg = \"I'm a teapot\";\n foo.unwrap_or_else(|| panic!(\"Err {}: {}\", err_code, err_msg));\n ```\n", @@ -2761,8 +2803,8 @@ { "id": "clone_on_copy", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 677 + "path": "src/methods/mod.rs", + "line": 679 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for usage of `.clone()` on a `Copy` type.\n **Why is this bad?** The only reason `Copy` types implement `Clone` is for\n generics, not for using the `clone` method on a concrete type.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n 42u64.clone();\n ```\n", @@ -2774,8 +2816,8 @@ { "id": "clone_on_ref_ptr", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 702 + "path": "src/methods/mod.rs", + "line": 704 }, "group": "clippy::restriction", "docs": " **What it does:** Checks for usage of `.clone()` on a ref-counted pointer, (`Rc`, `Arc`, `rc::Weak`, or `sync::Weak`), and suggests calling Clone via unified\n function syntax instead (e.g., `Rc::clone(foo)`).\n\n **Why is this bad?** Calling '.clone()' on an Rc, Arc, or Weak\n can obscure the fact that only the pointer is being cloned, not the underlying\n data.\n\n **Example:**\n ```rust\n # use std::rc::Rc;\n let x = Rc::new(1);\n\n // Bad\n x.clone();\n\n // Good\n Rc::clone(&x);\n ```\n", @@ -2787,8 +2829,8 @@ { "id": "clone_double_ref", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 724 + "path": "src/methods/mod.rs", + "line": 726 }, "group": "clippy::correctness", "docs": " **What it does:** Checks for usage of `.clone()` on an `&&T`.\n **Why is this bad?** Cloning an `&&T` copies the inner `&T`, instead of\n cloning the underlying `T`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn main() {\n let x = vec![1];\n let y = &&x;\n let z = y.clone();\n println!(\"{:p} {:p}\", *y, z); // prints out the same pointer\n }\n ```\n", @@ -2800,8 +2842,8 @@ { "id": "inefficient_to_string", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 747 + "path": "src/methods/mod.rs", + "line": 749 }, "group": "clippy::pedantic", "docs": " **What it does:** Checks for usage of `.to_string()` on an `&&T` where `T` implements `ToString` directly (like `&&str` or `&&String`).\n\n **Why is this bad?** This bypasses the specialized implementation of\n `ToString` and instead goes through the more expensive string formatting\n facilities.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Generic implementation for `T: Display` is used (slow)\n [\"foo\", \"bar\"].iter().map(|s| s.to_string());\n\n // OK, the specialized impl is used\n [\"foo\", \"bar\"].iter().map(|&s| s.to_string());\n ```\n", @@ -2813,8 +2855,8 @@ { "id": "new_ret_no_self", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 808 + "path": "src/methods/mod.rs", + "line": 810 }, "group": "clippy::style", "docs": " **What it does:** Checks for `new` not returning a type that contains `Self`.\n **Why is this bad?** As a convention, `new` methods are used to make a new\n instance of a type.\n\n **Known problems:** None.\n\n **Example:**\n In an impl block:\n ```rust\n # struct Foo;\n # struct NotAFoo;\n impl Foo {\n fn new() -> NotAFoo {\n # NotAFoo\n }\n }\n ```\n\n ```rust\n # struct Foo;\n struct Bar(Foo);\n impl Foo {\n // Bad. The type name must contain `Self`\n fn new() -> Bar {\n # Bar(Foo)\n }\n }\n ```\n\n ```rust\n # struct Foo;\n # struct FooError;\n impl Foo {\n // Good. Return type contains `Self`\n fn new() -> Result {\n # Ok(Foo)\n }\n }\n ```\n\n Or in a trait definition:\n ```rust\n pub trait Trait {\n // Bad. The type name must contain `Self`\n fn new();\n }\n ```\n\n ```rust\n pub trait Trait {\n // Good. Return type contains `Self`\n fn new() -> Self;\n }\n ```\n", @@ -2826,8 +2868,8 @@ { "id": "single_char_pattern", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 829 + "path": "src/methods/mod.rs", + "line": 831 }, "group": "clippy::perf", "docs": " **What it does:** Checks for string methods that receive a single-character `str` as an argument, e.g., `_.split(\"x\")`.\n\n **Why is this bad?** Performing these methods using a `char` is faster than\n using a `str`.\n\n **Known problems:** Does not catch multi-byte unicode characters.\n\n **Example:**\n ```rust,ignore\n // Bad\n _.split(\"x\");\n\n // Good\n _.split('x');\n", @@ -2839,8 +2881,8 @@ { "id": "iterator_step_by_zero", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 848 + "path": "src/methods/mod.rs", + "line": 850 }, "group": "clippy::correctness", "docs": " **What it does:** Checks for calling `.step_by(0)` on iterators which panics.\n **Why is this bad?** This very much looks like an oversight. Use `panic!()` instead if you\n actually intend to panic.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,should_panic\n for x in (0..100).step_by(0) {\n //..\n }\n ```\n", @@ -2852,8 +2894,8 @@ { "id": "iter_nth_zero", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 876 + "path": "src/methods/mod.rs", + "line": 878 }, "group": "clippy::style", "docs": " **What it does:** Checks for the use of `iter.nth(0)`.\n **Why is this bad?** `iter.next()` is equivalent to\n `iter.nth(0)`, as they both consume the next element,\n but is more readable.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # use std::collections::HashSet;\n // Bad\n # let mut s = HashSet::new();\n # s.insert(1);\n let x = s.iter().nth(0);\n\n // Good\n # let mut s = HashSet::new();\n # s.insert(1);\n let x = s.iter().next();\n ```\n", @@ -2865,8 +2907,8 @@ { "id": "iter_nth", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 902 + "path": "src/methods/mod.rs", + "line": 904 }, "group": "clippy::perf", "docs": " **What it does:** Checks for use of `.iter().nth()` (and the related `.iter_mut().nth()`) on standard library types with O(1) element access.\n\n **Why is this bad?** `.get()` and `.get_mut()` are more efficient and more\n readable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let some_vec = vec![0, 1, 2, 3];\n let bad_vec = some_vec.iter().nth(3);\n let bad_slice = &some_vec[..].iter().nth(3);\n ```\n The correct use would be:\n ```rust\n let some_vec = vec![0, 1, 2, 3];\n let bad_vec = some_vec.get(3);\n let bad_slice = &some_vec[..].get(3);\n ```\n", @@ -2878,8 +2920,8 @@ { "id": "iter_skip_next", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 926 + "path": "src/methods/mod.rs", + "line": 928 }, "group": "clippy::style", "docs": " **What it does:** Checks for use of `.skip(x).next()` on iterators.\n **Why is this bad?** `.nth(x)` is cleaner\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let some_vec = vec![0, 1, 2, 3];\n let bad_vec = some_vec.iter().skip(3).next();\n let bad_slice = &some_vec[..].iter().skip(3).next();\n ```\n The correct use would be:\n ```rust\n let some_vec = vec![0, 1, 2, 3];\n let bad_vec = some_vec.iter().nth(3);\n let bad_slice = &some_vec[..].iter().nth(3);\n ```\n", @@ -2891,8 +2933,8 @@ { "id": "get_unwrap", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 959 + "path": "src/methods/mod.rs", + "line": 961 }, "group": "clippy::restriction", "docs": " **What it does:** Checks for use of `.get().unwrap()` (or `.get_mut().unwrap`) on a standard library type which implements `Index`\n\n **Why is this bad?** Using the Index trait (`[]`) is more clear and more\n concise.\n\n **Known problems:** Not a replacement for error handling: Using either\n `.unwrap()` or the Index trait (`[]`) carries the risk of causing a `panic`\n if the value being accessed is `None`. If the use of `.get().unwrap()` is a\n temporary placeholder for dealing with the `Option` type, then this does\n not mitigate the need for error handling. If there is a chance that `.get()`\n will be `None` in your program, then it is advisable that the `None` case\n is handled in a future refactor instead of using `.unwrap()` or the Index\n trait.\n\n **Example:**\n ```rust\n let mut some_vec = vec![0, 1, 2, 3];\n let last = some_vec.get(3).unwrap();\n *some_vec.get_mut(0).unwrap() = 1;\n ```\n The correct use would be:\n ```rust\n let mut some_vec = vec![0, 1, 2, 3];\n let last = some_vec[3];\n some_vec[0] = 1;\n ```\n", @@ -2904,8 +2946,8 @@ { "id": "string_extend_chars", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 988 + "path": "src/methods/mod.rs", + "line": 990 }, "group": "clippy::style", "docs": " **What it does:** Checks for the use of `.extend(s.chars())` where s is a `&str` or `String`.\n\n **Why is this bad?** `.push_str(s)` is clearer\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let abc = \"abc\";\n let def = String::from(\"def\");\n let mut s = String::new();\n s.extend(abc.chars());\n s.extend(def.chars());\n ```\n The correct use would be:\n ```rust\n let abc = \"abc\";\n let def = String::from(\"def\");\n let mut s = String::new();\n s.push_str(abc);\n s.push_str(&def);\n ```\n", @@ -2917,8 +2959,8 @@ { "id": "iter_cloned_collect", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1011 + "path": "src/methods/mod.rs", + "line": 1013 }, "group": "clippy::style", "docs": " **What it does:** Checks for the use of `.cloned().collect()` on slice to create a `Vec`.\n\n **Why is this bad?** `.to_vec()` is clearer\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let s = [1, 2, 3, 4, 5];\n let s2: Vec = s[..].iter().cloned().collect();\n ```\n The better use would be:\n ```rust\n let s = [1, 2, 3, 4, 5];\n let s2: Vec = s.to_vec();\n ```\n", @@ -2930,8 +2972,8 @@ { "id": "chars_last_cmp", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1035 + "path": "src/methods/mod.rs", + "line": 1037 }, "group": "clippy::style", "docs": " **What it does:** Checks for usage of `_.chars().last()` or `_.chars().next_back()` on a `str` to check if it ends with a given char.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.ends_with(_)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let name = \"_\";\n\n // Bad\n name.chars().last() == Some('_') || name.chars().next_back() == Some('-');\n\n // Good\n name.ends_with('_') || name.ends_with('-');\n ```\n", @@ -2940,8 +2982,8 @@ { "id": "useless_asref", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1060 + "path": "src/methods/mod.rs", + "line": 1062 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for usage of `.as_ref()` or `.as_mut()` where the types before and after the call are the same.\n\n **Why is this bad?** The call is unnecessary.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn do_stuff(x: &[i32]) {}\n let x: &[i32] = &[1, 2, 3, 4, 5];\n do_stuff(x.as_ref());\n ```\n The correct use would be:\n ```rust\n # fn do_stuff(x: &[i32]) {}\n let x: &[i32] = &[1, 2, 3, 4, 5];\n do_stuff(x);\n ```\n", @@ -2953,8 +2995,8 @@ { "id": "unnecessary_fold", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1082 + "path": "src/methods/mod.rs", + "line": 1084 }, "group": "clippy::style", "docs": " **What it does:** Checks for using `fold` when a more succinct alternative exists. Specifically, this checks for `fold`s which could be replaced by `any`, `all`,\n `sum` or `product`.\n\n **Why is this bad?** Readability.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let _ = (0..3).fold(false, |acc, x| acc || x > 2);\n ```\n This could be written as:\n ```rust\n let _ = (0..3).any(|x| x > 2);\n ```\n", @@ -2966,8 +3008,8 @@ { "id": "unnecessary_filter_map", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1111 + "path": "src/methods/mod.rs", + "line": 1113 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for `filter_map` calls which could be replaced by `filter` or `map`. More specifically it checks if the closure provided is only performing one of the\n filter or map operations and suggests the appropriate option.\n\n **Why is this bad?** Complexity. The intent is also clearer if only a single\n operation is being performed.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n let _ = (0..3).filter_map(|x| if x > 2 { Some(x) } else { None });\n\n // As there is no transformation of the argument this could be written as:\n let _ = (0..3).filter(|&x| x > 2);\n ```\n\n ```rust\n let _ = (0..4).filter_map(|x| Some(x + 1));\n\n // As there is no conditional check on the argument this could be written as:\n let _ = (0..4).map(|x| x + 1);\n ```\n", @@ -2979,8 +3021,8 @@ { "id": "into_iter_on_ref", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1135 + "path": "src/methods/mod.rs", + "line": 1137 }, "group": "clippy::style", "docs": " **What it does:** Checks for `into_iter` calls on references which should be replaced by `iter` or `iter_mut`.\n\n **Why is this bad?** Readability. Calling `into_iter` on a reference will not move out its\n content into the resulting iterator, which is confusing. It is better just call `iter` or\n `iter_mut` directly.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n // Bad\n let _ = (&vec![3, 4, 5]).into_iter();\n\n // Good\n let _ = (&vec![3, 4, 5]).iter();\n ```\n", @@ -2992,8 +3034,8 @@ { "id": "suspicious_map", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1154 + "path": "src/methods/mod.rs", + "line": 1156 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for calls to `map` followed by a `count`.\n **Why is this bad?** It looks suspicious. Maybe `map` was confused with `filter`.\n If the `map` call is intentional, this should be rewritten. Or, if you intend to\n drive the iterator to completion, you can just use `for_each` instead.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n let _ = (0..3).map(|x| x + 2).count();\n ```\n", @@ -3005,8 +3047,8 @@ { "id": "uninit_assumed_init", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1186 + "path": "src/methods/mod.rs", + "line": 1188 }, "group": "clippy::correctness", "docs": " **What it does:** Checks for `MaybeUninit::uninit().assume_init()`.\n **Why is this bad?** For most types, this is undefined behavior.\n\n **Known problems:** For now, we accept empty tuples and tuples / arrays\n of `MaybeUninit`. There may be other types that allow uninitialized\n data, but those are not yet rigorously defined.\n\n **Example:**\n\n ```rust\n // Beware the UB\n use std::mem::MaybeUninit;\n\n let _: usize = unsafe { MaybeUninit::uninit().assume_init() };\n ```\n\n Note that the following is OK:\n\n ```rust\n use std::mem::MaybeUninit;\n\n let _: [MaybeUninit; 5] = unsafe {\n MaybeUninit::uninit().assume_init()\n };\n ```\n", @@ -3018,8 +3060,8 @@ { "id": "manual_saturating_arithmetic", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1213 + "path": "src/methods/mod.rs", + "line": 1215 }, "group": "clippy::style", "docs": " **What it does:** Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`.\n **Why is this bad?** These can be written simply with `saturating_add/sub` methods.\n\n **Example:**\n\n ```rust\n # let y: u32 = 0;\n # let x: u32 = 100;\n let add = x.checked_add(y).unwrap_or(u32::MAX);\n let sub = x.checked_sub(y).unwrap_or(u32::MIN);\n ```\n\n can be written using dedicated methods for saturating addition/subtraction as:\n\n ```rust\n # let y: u32 = 0;\n # let x: u32 = 100;\n let add = x.saturating_add(y);\n let sub = x.saturating_sub(y);\n ```\n", @@ -3031,8 +3073,8 @@ { "id": "zst_offset", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1230 + "path": "src/methods/mod.rs", + "line": 1232 }, "group": "clippy::correctness", "docs": " **What it does:** Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to zero-sized types\n\n **Why is this bad?** This is a no-op, and likely unintended\n\n **Known problems:** None\n\n **Example:**\n ```rust\n unsafe { (&() as *const ()).offset(1) };\n ```\n", @@ -3044,8 +3086,8 @@ { "id": "filetype_is_file", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1270 + "path": "src/methods/mod.rs", + "line": 1272 }, "group": "clippy::restriction", "docs": " **What it does:** Checks for `FileType::is_file()`.\n **Why is this bad?** When people testing a file type with `FileType::is_file`\n they are testing whether a path is something they can get bytes from. But\n `is_file` doesn't cover special file types in unix-like systems, and doesn't cover\n symlink in windows. Using `!FileType::is_dir()` is a better way to that intention.\n\n **Example:**\n\n ```rust\n # || {\n let metadata = std::fs::metadata(\"foo.txt\")?;\n let filetype = metadata.file_type();\n\n if filetype.is_file() {\n // read file\n }\n # Ok::<_, std::io::Error>(())\n # };\n ```\n\n should be written as:\n\n ```rust\n # || {\n let metadata = std::fs::metadata(\"foo.txt\")?;\n let filetype = metadata.file_type();\n\n if !filetype.is_dir() {\n // read file\n }\n # Ok::<_, std::io::Error>(())\n # };\n ```\n", @@ -3057,8 +3099,8 @@ { "id": "option_as_ref_deref", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1295 + "path": "src/methods/mod.rs", + "line": 1297 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str).\n **Why is this bad?** Readability, this can be written more concisely as\n `_.as_deref()`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let opt = Some(\"\".to_string());\n opt.as_ref().map(String::as_str)\n # ;\n ```\n Can be written as\n ```rust\n # let opt = Some(\"\".to_string());\n opt.as_deref()\n # ;\n ```\n", @@ -3070,8 +3112,8 @@ { "id": "iter_next_slice", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1321 + "path": "src/methods/mod.rs", + "line": 1323 }, "group": "clippy::style", "docs": " **What it does:** Checks for usage of `iter().next()` on a Slice or an Array\n **Why is this bad?** These can be shortened into `.get()`\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let a = [1, 2, 3];\n # let b = vec![1, 2, 3];\n a[2..].iter().next();\n b.iter().next();\n ```\n should be written as:\n ```rust\n # let a = [1, 2, 3];\n # let b = vec![1, 2, 3];\n a.get(2);\n b.get(0);\n ```\n", @@ -3083,8 +3125,8 @@ { "id": "single_char_add_str", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1346 + "path": "src/methods/mod.rs", + "line": 1348 }, "group": "clippy::style", "docs": " **What it does:** Warns when using `push_str`/`insert_str` with a single-character string literal where `push`/`insert` with a `char` would work fine.\n\n **Why is this bad?** It's less clear that we are pushing a single character.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n let mut string = String::new();\n string.insert_str(0, \"R\");\n string.push_str(\"R\");\n ```\n Could be written as\n ```rust\n let mut string = String::new();\n string.insert(0, 'R');\n string.push('R');\n ```\n", @@ -3096,8 +3138,8 @@ { "id": "unnecessary_lazy_evaluations", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1382 + "path": "src/methods/mod.rs", + "line": 1384 }, "group": "clippy::style", "docs": " **What it does:** As the counterpart to `or_fun_call`, this lint looks for unnecessary lazily evaluated closures on `Option` and `Result`.\n\n This lint suggests changing the following functions, when eager evaluation results in\n simpler code:\n - `unwrap_or_else` to `unwrap_or`\n - `and_then` to `and`\n - `or_else` to `or`\n - `get_or_insert_with` to `get_or_insert`\n - `ok_or_else` to `ok_or`\n\n **Why is this bad?** Using eager evaluation is shorter and simpler in some cases.\n\n **Known problems:** It is possible, but not recommended for `Deref` and `Index` to have\n side effects. Eagerly evaluating them can change the semantics of the program.\n\n **Example:**\n\n ```rust\n // example code where clippy issues a warning\n let opt: Option = None;\n\n opt.unwrap_or_else(|| 42);\n ```\n Use instead:\n ```rust\n let opt: Option = None;\n\n opt.unwrap_or(42);\n ```\n", @@ -3109,8 +3151,8 @@ { "id": "map_collect_result_unit", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1403 + "path": "src/methods/mod.rs", + "line": 1405 }, "group": "clippy::style", "docs": " **What it does:** Checks for usage of `_.map(_).collect::()`.\n **Why is this bad?** Using `try_for_each` instead is more readable and idiomatic.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n (0..3).map(|t| Err(t)).collect::>();\n ```\n Use instead:\n ```rust\n (0..3).try_for_each(|t| Err(t));\n ```\n", @@ -3122,8 +3164,8 @@ { "id": "from_iter_instead_of_collect", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1436 + "path": "src/methods/mod.rs", + "line": 1438 }, "group": "clippy::style", "docs": " **What it does:** Checks for `from_iter()` function calls on types that implement the `FromIterator` trait.\n\n **Why is this bad?** It is recommended style to use collect. See\n [FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html)\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n use std::iter::FromIterator;\n\n let five_fives = std::iter::repeat(5).take(5);\n\n let v = Vec::from_iter(five_fives);\n\n assert_eq!(v, vec![5, 5, 5, 5, 5]);\n ```\n Use instead:\n ```rust\n let five_fives = std::iter::repeat(5).take(5);\n\n let v: Vec = five_fives.collect();\n\n assert_eq!(v, vec![5, 5, 5, 5, 5]);\n ```\n", @@ -3135,8 +3177,8 @@ { "id": "inspect_for_each", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1466 + "path": "src/methods/mod.rs", + "line": 1468 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for usage of `inspect().for_each()`.\n **Why is this bad?** It is the same as performing the computation\n inside `inspect` at the beginning of the closure in `for_each`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n [1,2,3,4,5].iter()\n .inspect(|&x| println!(\"inspect the number: {}\", x))\n .for_each(|&x| {\n assert!(x >= 0);\n });\n ```\n Can be written as\n ```rust\n [1,2,3,4,5].iter()\n .for_each(|&x| {\n println!(\"inspect the number: {}\", x);\n assert!(x >= 0);\n });\n ```\n", @@ -3148,8 +3190,8 @@ { "id": "filter_map_identity", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1489 + "path": "src/methods/mod.rs", + "line": 1491 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for usage of `filter_map(|x| x)`.\n **Why is this bad?** Readability, this can be written more concisely by using `flatten`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # let iter = vec![Some(1)].into_iter();\n iter.filter_map(|x| x);\n ```\n Use instead:\n ```rust\n # let iter = vec![Some(1)].into_iter();\n iter.flatten();\n ```\n", @@ -3161,8 +3203,8 @@ { "id": "bytes_nth", "id_span": { - "path": "clippy_lints/src/methods/mod.rs", - "line": 1511 + "path": "src/methods/mod.rs", + "line": 1513 }, "group": "clippy::style", "docs": " **What it does:** Checks for the use of `.bytes().nth()`.\n **Why is this bad?** `.as_bytes().get()` is more efficient and more\n readable.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let _ = \"Hello\".bytes().nth(3);\n\n // Good\n let _ = \"Hello\".as_bytes().get(3);\n ```\n", @@ -3171,10 +3213,36 @@ "applicability": "MachineApplicable" } }, + { + "id": "implicit_clone", + "id_span": { + "path": "src/methods/mod.rs", + "line": 1539 + }, + "group": "clippy::pedantic", + "docs": " **What it does:** Checks for the usage of `_.to_owned()`, `vec.to_vec()`, or similar when calling `_.clone()` would be clearer.\n **Why is this bad?** These methods do the same thing as `_.clone()` but may be confusing as\n to why we are calling `to_vec` on something that is already a `Vec` or calling `to_owned` on something that is already owned.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let a = vec![1, 2, 3];\n let b = a.to_vec();\n let c = a.to_owned();\n ```\n Use instead:\n ```rust\n let a = vec![1, 2, 3];\n let b = a.clone();\n let c = a.clone();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, + { + "id": "iter_count", + "id_span": { + "path": "src/methods/mod.rs", + "line": 1565 + }, + "group": "clippy::complexity", + "docs": " **What it does:** Checks for the use of `.iter().count()`.\n **Why is this bad?** `.len()` is more efficient and more\n readable.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let some_vec = vec![0, 1, 2, 3];\n let _ = some_vec.iter().count();\n let _ = &some_vec[..].iter().count();\n\n // Good\n let some_vec = vec![0, 1, 2, 3];\n let _ = some_vec.len();\n let _ = &some_vec[..].len();\n ```\n", + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } + }, { "id": "min_max", "id_span": { - "path": "clippy_lints/src/minmax.rs", + "path": "src/minmax.rs", "line": 28 }, "group": "clippy::correctness", @@ -3187,7 +3255,7 @@ { "id": "toplevel_ref_arg", "id_span": { - "path": "clippy_lints/src/misc.rs", + "path": "src/misc.rs", "line": 53 }, "group": "clippy::style", @@ -3200,7 +3268,7 @@ { "id": "cmp_nan", "id_span": { - "path": "clippy_lints/src/misc.rs", + "path": "src/misc.rs", "line": 76 }, "group": "clippy::correctness", @@ -3213,7 +3281,7 @@ { "id": "float_cmp", "id_span": { - "path": "clippy_lints/src/misc.rs", + "path": "src/misc.rs", "line": 109 }, "group": "clippy::correctness", @@ -3223,7 +3291,7 @@ { "id": "cmp_owned", "id_span": { - "path": "clippy_lints/src/misc.rs", + "path": "src/misc.rs", "line": 136 }, "group": "clippy::perf", @@ -3236,7 +3304,7 @@ { "id": "modulo_one", "id_span": { - "path": "clippy_lints/src/misc.rs", + "path": "src/misc.rs", "line": 159 }, "group": "clippy::correctness", @@ -3249,7 +3317,7 @@ { "id": "used_underscore_binding", "id_span": { - "path": "clippy_lints/src/misc.rs", + "path": "src/misc.rs", "line": 181 }, "group": "clippy::pedantic", @@ -3262,7 +3330,7 @@ { "id": "short_circuit_statement", "id_span": { - "path": "clippy_lints/src/misc.rs", + "path": "src/misc.rs", "line": 201 }, "group": "clippy::complexity", @@ -3275,7 +3343,7 @@ { "id": "zero_ptr", "id_span": { - "path": "clippy_lints/src/misc.rs", + "path": "src/misc.rs", "line": 223 }, "group": "clippy::style", @@ -3288,7 +3356,7 @@ { "id": "float_cmp_const", "id_span": { - "path": "clippy_lints/src/misc.rs", + "path": "src/misc.rs", "line": 254 }, "group": "clippy::restriction", @@ -3298,7 +3366,7 @@ { "id": "unneeded_field_pattern", "id_span": { - "path": "clippy_lints/src/misc_early.rs", + "path": "src/misc_early.rs", "line": 44 }, "group": "clippy::restriction", @@ -3311,7 +3379,7 @@ { "id": "duplicate_underscore_argument", "id_span": { - "path": "clippy_lints/src/misc_early.rs", + "path": "src/misc_early.rs", "line": 65 }, "group": "clippy::style", @@ -3324,7 +3392,7 @@ { "id": "double_neg", "id_span": { - "path": "clippy_lints/src/misc_early.rs", + "path": "src/misc_early.rs", "line": 83 }, "group": "clippy::style", @@ -3337,7 +3405,7 @@ { "id": "mixed_case_hex_literals", "id_span": { - "path": "clippy_lints/src/misc_early.rs", + "path": "src/misc_early.rs", "line": 104 }, "group": "clippy::style", @@ -3350,7 +3418,7 @@ { "id": "unseparated_literal_suffix", "id_span": { - "path": "clippy_lints/src/misc_early.rs", + "path": "src/misc_early.rs", "line": 125 }, "group": "clippy::pedantic", @@ -3363,7 +3431,7 @@ { "id": "zero_prefixed_literal", "id_span": { - "path": "clippy_lints/src/misc_early.rs", + "path": "src/misc_early.rs", "line": 163 }, "group": "clippy::complexity", @@ -3376,7 +3444,7 @@ { "id": "builtin_type_shadow", "id_span": { - "path": "clippy_lints/src/misc_early.rs", + "path": "src/misc_early.rs", "line": 184 }, "group": "clippy::style", @@ -3389,7 +3457,7 @@ { "id": "redundant_pattern", "id_span": { - "path": "clippy_lints/src/misc_early.rs", + "path": "src/misc_early.rs", "line": 213 }, "group": "clippy::style", @@ -3402,7 +3470,7 @@ { "id": "unneeded_wildcard_pattern", "id_span": { - "path": "clippy_lints/src/misc_early.rs", + "path": "src/misc_early.rs", "line": 247 }, "group": "clippy::complexity", @@ -3415,7 +3483,7 @@ { "id": "missing_const_for_fn", "id_span": { - "path": "clippy_lints/src/missing_const_for_fn.rs", + "path": "src/missing_const_for_fn.rs", "line": 72 }, "group": "clippy::nursery", @@ -3428,7 +3496,7 @@ { "id": "missing_docs_in_private_items", "id_span": { - "path": "clippy_lints/src/missing_doc.rs", + "path": "src/missing_doc.rs", "line": 29 }, "group": "clippy::restriction", @@ -3441,7 +3509,7 @@ { "id": "missing_inline_in_public_items", "id_span": { - "path": "clippy_lints/src/missing_inline.rs", + "path": "src/missing_inline.rs", "line": 55 }, "group": "clippy::restriction", @@ -3454,7 +3522,7 @@ { "id": "modulo_arithmetic", "id_span": { - "path": "clippy_lints/src/modulo_arithmetic.rs", + "path": "src/modulo_arithmetic.rs", "line": 26 }, "group": "clippy::restriction", @@ -3467,7 +3535,7 @@ { "id": "multiple_crate_versions", "id_span": { - "path": "clippy_lints/src/multiple_crate_versions.rs", + "path": "src/multiple_crate_versions.rs", "line": 32 }, "group": "clippy::cargo", @@ -3480,7 +3548,7 @@ { "id": "mutable_key_type", "id_span": { - "path": "clippy_lints/src/mut_key.rs", + "path": "src/mut_key.rs", "line": 50 }, "group": "clippy::correctness", @@ -3493,7 +3561,7 @@ { "id": "mut_mut", "id_span": { - "path": "clippy_lints/src/mut_mut.rs", + "path": "src/mut_mut.rs", "line": 24 }, "group": "clippy::pedantic", @@ -3506,7 +3574,7 @@ { "id": "mut_mutex_lock", "id_span": { - "path": "clippy_lints/src/mut_mutex_lock.rs", + "path": "src/mut_mutex_lock.rs", "line": 40 }, "group": "clippy::style", @@ -3519,7 +3587,7 @@ { "id": "unnecessary_mut_passed", "id_span": { - "path": "clippy_lints/src/mut_reference.rs", + "path": "src/mut_reference.rs", "line": 25 }, "group": "clippy::style", @@ -3532,7 +3600,7 @@ { "id": "debug_assert_with_mut_call", "id_span": { - "path": "clippy_lints/src/mutable_debug_assertion.rs", + "path": "src/mutable_debug_assertion.rs", "line": 28 }, "group": "clippy::nursery", @@ -3545,7 +3613,7 @@ { "id": "mutex_atomic", "id_span": { - "path": "clippy_lints/src/mutex_atomic.rs", + "path": "src/mutex_atomic.rs", "line": 34 }, "group": "clippy::perf", @@ -3558,7 +3626,7 @@ { "id": "mutex_integer", "id_span": { - "path": "clippy_lints/src/mutex_atomic.rs", + "path": "src/mutex_atomic.rs", "line": 59 }, "group": "clippy::nursery", @@ -3571,7 +3639,7 @@ { "id": "needless_arbitrary_self_type", "id_span": { - "path": "clippy_lints/src/needless_arbitrary_self_type.rs", + "path": "src/needless_arbitrary_self_type.rs", "line": 55 }, "group": "clippy::complexity", @@ -3584,7 +3652,7 @@ { "id": "needless_bool", "id_span": { - "path": "clippy_lints/src/needless_bool.rs", + "path": "src/needless_bool.rs", "line": 38 }, "group": "clippy::complexity", @@ -3597,7 +3665,7 @@ { "id": "bool_comparison", "id_span": { - "path": "clippy_lints/src/needless_bool.rs", + "path": "src/needless_bool.rs", "line": 62 }, "group": "clippy::complexity", @@ -3610,7 +3678,7 @@ { "id": "needless_borrow", "id_span": { - "path": "clippy_lints/src/needless_borrow.rs", + "path": "src/needless_borrow.rs", "line": 32 }, "group": "clippy::nursery", @@ -3623,7 +3691,7 @@ { "id": "needless_borrowed_reference", "id_span": { - "path": "clippy_lints/src/needless_borrowed_ref.rs", + "path": "src/needless_borrowed_ref.rs", "line": 48 }, "group": "clippy::complexity", @@ -3636,7 +3704,7 @@ { "id": "needless_continue", "id_span": { - "path": "clippy_lints/src/needless_continue.rs", + "path": "src/needless_continue.rs", "line": 114 }, "group": "clippy::pedantic", @@ -3649,7 +3717,7 @@ { "id": "needless_pass_by_value", "id_span": { - "path": "clippy_lints/src/needless_pass_by_value.rs", + "path": "src/needless_pass_by_value.rs", "line": 50 }, "group": "clippy::pedantic", @@ -3662,7 +3730,7 @@ { "id": "needless_question_mark", "id_span": { - "path": "clippy_lints/src/needless_question_mark.rs", + "path": "src/needless_question_mark.rs", "line": 57 }, "group": "clippy::complexity", @@ -3675,7 +3743,7 @@ { "id": "needless_update", "id_span": { - "path": "clippy_lints/src/needless_update.rs", + "path": "src/needless_update.rs", "line": 43 }, "group": "clippy::complexity", @@ -3688,7 +3756,7 @@ { "id": "neg_cmp_op_on_partial_ord", "id_span": { - "path": "clippy_lints/src/neg_cmp_op_on_partial_ord.rs", + "path": "src/neg_cmp_op_on_partial_ord.rs", "line": 41 }, "group": "clippy::complexity", @@ -3701,7 +3769,7 @@ { "id": "neg_multiply", "id_span": { - "path": "clippy_lints/src/neg_multiply.rs", + "path": "src/neg_multiply.rs", "line": 21 }, "group": "clippy::style", @@ -3714,7 +3782,7 @@ { "id": "new_without_default", "id_span": { - "path": "clippy_lints/src/new_without_default.rs", + "path": "src/new_without_default.rs", "line": 48 }, "group": "clippy::style", @@ -3727,7 +3795,7 @@ { "id": "no_effect", "id_span": { - "path": "clippy_lints/src/no_effect.rs", + "path": "src/no_effect.rs", "line": 22 }, "group": "clippy::complexity", @@ -3740,7 +3808,7 @@ { "id": "unnecessary_operation", "id_span": { - "path": "clippy_lints/src/no_effect.rs", + "path": "src/no_effect.rs", "line": 40 }, "group": "clippy::complexity", @@ -3753,7 +3821,7 @@ { "id": "declare_interior_mutable_const", "id_span": { - "path": "clippy_lints/src/non_copy_const.rs", + "path": "src/non_copy_const.rs", "line": 69 }, "group": "clippy::style", @@ -3763,7 +3831,7 @@ { "id": "borrow_interior_mutable_const", "id_span": { - "path": "clippy_lints/src/non_copy_const.rs", + "path": "src/non_copy_const.rs", "line": 110 }, "group": "clippy::style", @@ -3773,7 +3841,7 @@ { "id": "similar_names", "id_span": { - "path": "clippy_lints/src/non_expressive_names.rs", + "path": "src/non_expressive_names.rs", "line": 27 }, "group": "clippy::pedantic", @@ -3786,7 +3854,7 @@ { "id": "many_single_char_names", "id_span": { - "path": "clippy_lints/src/non_expressive_names.rs", + "path": "src/non_expressive_names.rs", "line": 45 }, "group": "clippy::style", @@ -3799,7 +3867,7 @@ { "id": "just_underscores_and_digits", "id_span": { - "path": "clippy_lints/src/non_expressive_names.rs", + "path": "src/non_expressive_names.rs", "line": 65 }, "group": "clippy::style", @@ -3812,7 +3880,7 @@ { "id": "nonsensical_open_options", "id_span": { - "path": "clippy_lints/src/open_options.rs", + "path": "src/open_options.rs", "line": 23 }, "group": "clippy::correctness", @@ -3825,7 +3893,7 @@ { "id": "option_env_unwrap", "id_span": { - "path": "clippy_lints/src/option_env_unwrap.rs", + "path": "src/option_env_unwrap.rs", "line": 28 }, "group": "clippy::correctness", @@ -3838,7 +3906,7 @@ { "id": "option_if_let_else", "id_span": { - "path": "clippy_lints/src/option_if_let_else.rs", + "path": "src/option_if_let_else.rs", "line": 59 }, "group": "clippy::pedantic", @@ -3851,7 +3919,7 @@ { "id": "overflow_check_conditional", "id_span": { - "path": "clippy_lints/src/overflow_check_conditional.rs", + "path": "src/overflow_check_conditional.rs", "line": 21 }, "group": "clippy::complexity", @@ -3864,7 +3932,7 @@ { "id": "panic_in_result_fn", "id_span": { - "path": "clippy_lints/src/panic_in_result_fn.rs", + "path": "src/panic_in_result_fn.rs", "line": 29 }, "group": "clippy::restriction", @@ -3877,7 +3945,7 @@ { "id": "panic", "id_span": { - "path": "clippy_lints/src/panic_unimplemented.rs", + "path": "src/panic_unimplemented.rs", "line": 19 }, "group": "clippy::restriction", @@ -3890,7 +3958,7 @@ { "id": "unimplemented", "id_span": { - "path": "clippy_lints/src/panic_unimplemented.rs", + "path": "src/panic_unimplemented.rs", "line": 35 }, "group": "clippy::restriction", @@ -3903,7 +3971,7 @@ { "id": "todo", "id_span": { - "path": "clippy_lints/src/panic_unimplemented.rs", + "path": "src/panic_unimplemented.rs", "line": 51 }, "group": "clippy::restriction", @@ -3916,7 +3984,7 @@ { "id": "unreachable", "id_span": { - "path": "clippy_lints/src/panic_unimplemented.rs", + "path": "src/panic_unimplemented.rs", "line": 67 }, "group": "clippy::restriction", @@ -3929,7 +3997,7 @@ { "id": "partialeq_ne_impl", "id_span": { - "path": "clippy_lints/src/partialeq_ne_impl.rs", + "path": "src/partialeq_ne_impl.rs", "line": 27 }, "group": "clippy::complexity", @@ -3942,7 +4010,7 @@ { "id": "trivially_copy_pass_by_ref", "id_span": { - "path": "clippy_lints/src/pass_by_ref_or_value.rs", + "path": "src/pass_by_ref_or_value.rs", "line": 52 }, "group": "clippy::pedantic", @@ -3955,7 +4023,7 @@ { "id": "large_types_passed_by_value", "id_span": { - "path": "clippy_lints/src/pass_by_ref_or_value.rs", + "path": "src/pass_by_ref_or_value.rs", "line": 84 }, "group": "clippy::pedantic", @@ -3968,7 +4036,7 @@ { "id": "path_buf_push_overwrite", "id_span": { - "path": "clippy_lints/src/path_buf_push_overwrite.rs", + "path": "src/path_buf_push_overwrite.rs", "line": 36 }, "group": "clippy::nursery", @@ -3981,7 +4049,7 @@ { "id": "pattern_type_mismatch", "id_span": { - "path": "clippy_lints/src/pattern_type_mismatch.rs", + "path": "src/pattern_type_mismatch.rs", "line": 79 }, "group": "clippy::restriction", @@ -3994,7 +4062,7 @@ { "id": "precedence", "id_span": { - "path": "clippy_lints/src/precedence.rs", + "path": "src/precedence.rs", "line": 44 }, "group": "clippy::complexity", @@ -4007,7 +4075,7 @@ { "id": "ptr_arg", "id_span": { - "path": "clippy_lints/src/ptr.rs", + "path": "src/ptr.rs", "line": 69 }, "group": "clippy::style", @@ -4020,7 +4088,7 @@ { "id": "cmp_null", "id_span": { - "path": "clippy_lints/src/ptr.rs", + "path": "src/ptr.rs", "line": 95 }, "group": "clippy::style", @@ -4033,7 +4101,7 @@ { "id": "mut_from_ref", "id_span": { - "path": "clippy_lints/src/ptr.rs", + "path": "src/ptr.rs", "line": 117 }, "group": "clippy::correctness", @@ -4046,7 +4114,7 @@ { "id": "ptr_eq", "id_span": { - "path": "clippy_lints/src/ptr_eq.rs", + "path": "src/ptr_eq.rs", "line": 32 }, "group": "clippy::style", @@ -4059,7 +4127,7 @@ { "id": "ptr_offset_with_cast", "id_span": { - "path": "clippy_lints/src/ptr_offset_with_cast.rs", + "path": "src/ptr_offset_with_cast.rs", "line": 40 }, "group": "clippy::complexity", @@ -4072,7 +4140,7 @@ { "id": "question_mark", "id_span": { - "path": "clippy_lints/src/question_mark.rs", + "path": "src/question_mark.rs", "line": 34 }, "group": "clippy::style", @@ -4085,7 +4153,7 @@ { "id": "range_zip_with_len", "id_span": { - "path": "clippy_lints/src/ranges.rs", + "path": "src/ranges.rs", "line": 40 }, "group": "clippy::complexity", @@ -4098,7 +4166,7 @@ { "id": "range_plus_one", "id_span": { - "path": "clippy_lints/src/ranges.rs", + "path": "src/ranges.rs", "line": 74 }, "group": "clippy::pedantic", @@ -4111,7 +4179,7 @@ { "id": "range_minus_one", "id_span": { - "path": "clippy_lints/src/ranges.rs", + "path": "src/ranges.rs", "line": 99 }, "group": "clippy::pedantic", @@ -4124,7 +4192,7 @@ { "id": "reversed_empty_ranges", "id_span": { - "path": "clippy_lints/src/ranges.rs", + "path": "src/ranges.rs", "line": 132 }, "group": "clippy::correctness", @@ -4137,7 +4205,7 @@ { "id": "manual_range_contains", "id_span": { - "path": "clippy_lints/src/ranges.rs", + "path": "src/ranges.rs", "line": 159 }, "group": "clippy::style", @@ -4150,7 +4218,7 @@ { "id": "redundant_clone", "id_span": { - "path": "clippy_lints/src/redundant_clone.rs", + "path": "src/redundant_clone.rs", "line": 63 }, "group": "clippy::perf", @@ -4163,7 +4231,7 @@ { "id": "redundant_closure_call", "id_span": { - "path": "clippy_lints/src/redundant_closure_call.rs", + "path": "src/redundant_closure_call.rs", "line": 32 }, "group": "clippy::complexity", @@ -4176,7 +4244,7 @@ { "id": "redundant_else", "id_span": { - "path": "clippy_lints/src/redundant_else.rs", + "path": "src/redundant_else.rs", "line": 37 }, "group": "clippy::pedantic", @@ -4189,7 +4257,7 @@ { "id": "redundant_field_names", "id_span": { - "path": "clippy_lints/src/redundant_field_names.rs", + "path": "src/redundant_field_names.rs", "line": 34 }, "group": "clippy::style", @@ -4202,7 +4270,7 @@ { "id": "redundant_pub_crate", "id_span": { - "path": "clippy_lints/src/redundant_pub_crate.rs", + "path": "src/redundant_pub_crate.rs", "line": 30 }, "group": "clippy::nursery", @@ -4215,7 +4283,7 @@ { "id": "redundant_slicing", "id_span": { - "path": "clippy_lints/src/redundant_slicing.rs", + "path": "src/redundant_slicing.rs", "line": 33 }, "group": "clippy::complexity", @@ -4228,7 +4296,7 @@ { "id": "redundant_static_lifetimes", "id_span": { - "path": "clippy_lints/src/redundant_static_lifetimes.rs", + "path": "src/redundant_static_lifetimes.rs", "line": 30 }, "group": "clippy::style", @@ -4241,7 +4309,7 @@ { "id": "ref_option_ref", "id_span": { - "path": "clippy_lints/src/ref_option_ref.rs", + "path": "src/ref_option_ref.rs", "line": 28 }, "group": "clippy::pedantic", @@ -4254,7 +4322,7 @@ { "id": "deref_addrof", "id_span": { - "path": "clippy_lints/src/reference.rs", + "path": "src/reference.rs", "line": 29 }, "group": "clippy::complexity", @@ -4267,7 +4335,7 @@ { "id": "ref_in_deref", "id_span": { - "path": "clippy_lints/src/reference.rs", + "path": "src/reference.rs", "line": 120 }, "group": "clippy::complexity", @@ -4280,7 +4348,7 @@ { "id": "invalid_regex", "id_span": { - "path": "clippy_lints/src/regex.rs", + "path": "src/regex.rs", "line": 25 }, "group": "clippy::correctness", @@ -4293,7 +4361,7 @@ { "id": "trivial_regex", "id_span": { - "path": "clippy_lints/src/regex.rs", + "path": "src/regex.rs", "line": 46 }, "group": "clippy::nursery", @@ -4306,7 +4374,7 @@ { "id": "repeat_once", "id_span": { - "path": "clippy_lints/src/repeat_once.rs", + "path": "src/repeat_once.rs", "line": 33 }, "group": "clippy::complexity", @@ -4319,7 +4387,7 @@ { "id": "let_and_return", "id_span": { - "path": "clippy_lints/src/returns.rs", + "path": "src/returns.rs", "line": 38 }, "group": "clippy::style", @@ -4332,7 +4400,7 @@ { "id": "needless_return", "id_span": { - "path": "clippy_lints/src/returns.rs", + "path": "src/returns.rs", "line": 63 }, "group": "clippy::style", @@ -4345,7 +4413,7 @@ { "id": "self_assignment", "id_span": { - "path": "clippy_lints/src/self_assignment.rs", + "path": "src/self_assignment.rs", "line": 29 }, "group": "clippy::correctness", @@ -4358,7 +4426,7 @@ { "id": "semicolon_if_nothing_returned", "id_span": { - "path": "clippy_lints/src/semicolon_if_nothing_returned.rs", + "path": "src/semicolon_if_nothing_returned.rs", "line": 30 }, "group": "clippy::restriction", @@ -4371,7 +4439,7 @@ { "id": "serde_api_misuse", "id_span": { - "path": "clippy_lints/src/serde_api.rs", + "path": "src/serde_api.rs", "line": 16 }, "group": "clippy::correctness", @@ -4384,7 +4452,7 @@ { "id": "shadow_same", "id_span": { - "path": "clippy_lints/src/shadow.rs", + "path": "src/shadow.rs", "line": 34 }, "group": "clippy::restriction", @@ -4397,7 +4465,7 @@ { "id": "shadow_reuse", "id_span": { - "path": "clippy_lints/src/shadow.rs", + "path": "src/shadow.rs", "line": 61 }, "group": "clippy::restriction", @@ -4410,7 +4478,7 @@ { "id": "shadow_unrelated", "id_span": { - "path": "clippy_lints/src/shadow.rs", + "path": "src/shadow.rs", "line": 93 }, "group": "clippy::pedantic", @@ -4423,7 +4491,7 @@ { "id": "single_component_path_imports", "id_span": { - "path": "clippy_lints/src/single_component_path_imports.rs", + "path": "src/single_component_path_imports.rs", "line": 32 }, "group": "clippy::style", @@ -4436,7 +4504,7 @@ { "id": "size_of_in_element_count", "id_span": { - "path": "clippy_lints/src/size_of_in_element_count.rs", + "path": "src/size_of_in_element_count.rs", "line": 31 }, "group": "clippy::correctness", @@ -4449,7 +4517,7 @@ { "id": "slow_vector_initialization", "id_span": { - "path": "clippy_lints/src/slow_vector_initialization.rs", + "path": "src/slow_vector_initialization.rs", "line": 37 }, "group": "clippy::perf", @@ -4462,7 +4530,7 @@ { "id": "stable_sort_primitive", "id_span": { - "path": "clippy_lints/src/stable_sort_primitive.rs", + "path": "src/stable_sort_primitive.rs", "line": 36 }, "group": "clippy::perf", @@ -4475,7 +4543,7 @@ { "id": "string_add_assign", "id_span": { - "path": "clippy_lints/src/strings.rs", + "path": "src/strings.rs", "line": 37 }, "group": "clippy::pedantic", @@ -4488,7 +4556,7 @@ { "id": "string_add", "id_span": { - "path": "clippy_lints/src/strings.rs", + "path": "src/strings.rs", "line": 65 }, "group": "clippy::restriction", @@ -4501,7 +4569,7 @@ { "id": "string_lit_as_bytes", "id_span": { - "path": "clippy_lints/src/strings.rs", + "path": "src/strings.rs", "line": 107 }, "group": "clippy::nursery", @@ -4514,7 +4582,7 @@ { "id": "string_from_utf8_as_bytes", "id_span": { - "path": "clippy_lints/src/strings.rs", + "path": "src/strings.rs", "line": 196 }, "group": "clippy::complexity", @@ -4527,7 +4595,7 @@ { "id": "str_to_string", "id_span": { - "path": "clippy_lints/src/strings.rs", + "path": "src/strings.rs", "line": 313 }, "group": "clippy::restriction", @@ -4540,7 +4608,7 @@ { "id": "string_to_string", "id_span": { - "path": "clippy_lints/src/strings.rs", + "path": "src/strings.rs", "line": 362 }, "group": "clippy::restriction", @@ -4553,7 +4621,7 @@ { "id": "suspicious_operation_groupings", "id_span": { - "path": "clippy_lints/src/suspicious_operation_groupings.rs", + "path": "src/suspicious_operation_groupings.rs", "line": 60 }, "group": "clippy::style", @@ -4566,7 +4634,7 @@ { "id": "suspicious_arithmetic_impl", "id_span": { - "path": "clippy_lints/src/suspicious_trait_impl.rs", + "path": "src/suspicious_trait_impl.rs", "line": 27 }, "group": "clippy::correctness", @@ -4579,7 +4647,7 @@ { "id": "suspicious_op_assign_impl", "id_span": { - "path": "clippy_lints/src/suspicious_trait_impl.rs", + "path": "src/suspicious_trait_impl.rs", "line": 48 }, "group": "clippy::correctness", @@ -4592,7 +4660,7 @@ { "id": "manual_swap", "id_span": { - "path": "clippy_lints/src/swap.rs", + "path": "src/swap.rs", "line": 36 }, "group": "clippy::complexity", @@ -4605,7 +4673,7 @@ { "id": "almost_swapped", "id_span": { - "path": "clippy_lints/src/swap.rs", + "path": "src/swap.rs", "line": 61 }, "group": "clippy::correctness", @@ -4618,7 +4686,7 @@ { "id": "tabs_in_doc_comments", "id_span": { - "path": "clippy_lints/src/tabs_in_doc_comments.rs", + "path": "src/tabs_in_doc_comments.rs", "line": 54 }, "group": "clippy::style", @@ -4631,7 +4699,7 @@ { "id": "temporary_assignment", "id_span": { - "path": "clippy_lints/src/temporary_assignment.rs", + "path": "src/temporary_assignment.rs", "line": 19 }, "group": "clippy::complexity", @@ -4644,7 +4712,7 @@ { "id": "to_digit_is_some", "id_span": { - "path": "clippy_lints/src/to_digit_is_some.rs", + "path": "src/to_digit_is_some.rs", "line": 27 }, "group": "clippy::style", @@ -4657,7 +4725,7 @@ { "id": "to_string_in_display", "id_span": { - "path": "clippy_lints/src/to_string_in_display.rs", + "path": "src/to_string_in_display.rs", "line": 40 }, "group": "clippy::correctness", @@ -4670,7 +4738,7 @@ { "id": "type_repetition_in_bounds", "id_span": { - "path": "clippy_lints/src/trait_bounds.rs", + "path": "src/trait_bounds.rs", "line": 28 }, "group": "clippy::pedantic", @@ -4683,7 +4751,7 @@ { "id": "trait_duplication_in_bounds", "id_span": { - "path": "clippy_lints/src/trait_bounds.rs", + "path": "src/trait_bounds.rs", "line": 57 }, "group": "clippy::pedantic", @@ -4696,8 +4764,8 @@ { "id": "wrong_transmute", "id_span": { - "path": "clippy_lints/src/transmute.rs", - "line": 29 + "path": "src/transmute/mod.rs", + "line": 34 }, "group": "clippy::correctness", "docs": " **What it does:** Checks for transmutes that can't ever be correct on any architecture.\n\n **Why is this bad?** It's basically guaranteed to be undefined behaviour.\n\n **Known problems:** When accessing C, users might want to store pointer\n sized objects in `extradata` arguments to save an allocation.\n\n **Example:**\n ```ignore\n let ptr: *const T = core::intrinsics::transmute('x')\n ```\n", @@ -4709,8 +4777,8 @@ { "id": "useless_transmute", "id_span": { - "path": "clippy_lints/src/transmute.rs", - "line": 48 + "path": "src/transmute/mod.rs", + "line": 53 }, "group": "clippy::nursery", "docs": " **What it does:** Checks for transmutes to the original type of the object and transmutes that could be a cast.\n\n **Why is this bad?** Readability. The code tricks people into thinking that\n something complex is going on.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n core::intrinsics::transmute(t); // where the result type is the same as `t`'s\n ```\n", @@ -4722,8 +4790,8 @@ { "id": "transmutes_expressible_as_ptr_casts", "id_span": { - "path": "clippy_lints/src/transmute.rs", - "line": 73 + "path": "src/transmute/mod.rs", + "line": 78 }, "group": "clippy::complexity", "docs": " **What it does:**Checks for transmutes that could be a pointer cast.\n **Why is this bad?** Readability. The code tricks people into thinking that\n something complex is going on.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # let p: *const [i32] = &[];\n unsafe { std::mem::transmute::<*const [i32], *const [u16]>(p) };\n ```\n Use instead:\n ```rust\n # let p: *const [i32] = &[];\n p as *const [u16];\n ```\n", @@ -4735,8 +4803,8 @@ { "id": "crosspointer_transmute", "id_span": { - "path": "clippy_lints/src/transmute.rs", - "line": 91 + "path": "src/transmute/mod.rs", + "line": 96 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for transmutes between a type `T` and `*T`.\n **Why is this bad?** It's easy to mistakenly transmute between a type and a\n pointer to that type.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n core::intrinsics::transmute(t) // where the result type is the same as\n // `*t` or `&t`'s\n ```\n", @@ -4748,8 +4816,8 @@ { "id": "transmute_ptr_to_ref", "id_span": { - "path": "clippy_lints/src/transmute.rs", - "line": 116 + "path": "src/transmute/mod.rs", + "line": 121 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for transmutes from a pointer to a reference.\n **Why is this bad?** This can always be rewritten with `&` and `*`.\n\n **Known problems:**\n - `mem::transmute` in statics and constants is stable from Rust 1.46.0,\n while dereferencing raw pointer is not stable yet.\n If you need to do this in those places,\n you would have to use `transmute` instead.\n\n **Example:**\n ```rust,ignore\n unsafe {\n let _: &T = std::mem::transmute(p); // where p: *const T\n }\n\n // can be written:\n let _: &T = &*p;\n ```\n", @@ -4761,8 +4829,8 @@ { "id": "transmute_int_to_char", "id_span": { - "path": "clippy_lints/src/transmute.rs", - "line": 147 + "path": "src/transmute/mod.rs", + "line": 152 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for transmutes from an integer to a `char`.\n **Why is this bad?** Not every integer is a Unicode scalar value.\n\n **Known problems:**\n - [`from_u32`] which this lint suggests using is slower than `transmute`\n as it needs to validate the input.\n If you are certain that the input is always a valid Unicode scalar value,\n use [`from_u32_unchecked`] which is as fast as `transmute`\n but has a semantically meaningful name.\n - You might want to handle `None` returned from [`from_u32`] instead of calling `unwrap`.\n\n [`from_u32`]: https://doc.rust-lang.org/std/char/fn.from_u32.html\n [`from_u32_unchecked`]: https://doc.rust-lang.org/std/char/fn.from_u32_unchecked.html\n\n **Example:**\n ```rust\n let x = 1_u32;\n unsafe {\n let _: char = std::mem::transmute(x); // where x: u32\n }\n\n // should be:\n let _ = std::char::from_u32(x).unwrap();\n ```\n", @@ -4774,8 +4842,8 @@ { "id": "transmute_bytes_to_str", "id_span": { - "path": "clippy_lints/src/transmute.rs", - "line": 178 + "path": "src/transmute/mod.rs", + "line": 183 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for transmutes from a `&[u8]` to a `&str`.\n **Why is this bad?** Not every byte slice is a valid UTF-8 string.\n\n **Known problems:**\n - [`from_utf8`] which this lint suggests using is slower than `transmute`\n as it needs to validate the input.\n If you are certain that the input is always a valid UTF-8,\n use [`from_utf8_unchecked`] which is as fast as `transmute`\n but has a semantically meaningful name.\n - You might want to handle errors returned from [`from_utf8`] instead of calling `unwrap`.\n\n [`from_utf8`]: https://doc.rust-lang.org/std/str/fn.from_utf8.html\n [`from_utf8_unchecked`]: https://doc.rust-lang.org/std/str/fn.from_utf8_unchecked.html\n\n **Example:**\n ```rust\n let b: &[u8] = &[1_u8, 2_u8];\n unsafe {\n let _: &str = std::mem::transmute(b); // where b: &[u8]\n }\n\n // should be:\n let _ = std::str::from_utf8(b).unwrap();\n ```\n", @@ -4787,8 +4855,8 @@ { "id": "transmute_int_to_bool", "id_span": { - "path": "clippy_lints/src/transmute.rs", - "line": 200 + "path": "src/transmute/mod.rs", + "line": 205 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for transmutes from an integer to a `bool`.\n **Why is this bad?** This might result in an invalid in-memory representation of a `bool`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = 1_u8;\n unsafe {\n let _: bool = std::mem::transmute(x); // where x: u8\n }\n\n // should be:\n let _: bool = x != 0;\n ```\n", @@ -4800,8 +4868,8 @@ { "id": "transmute_int_to_float", "id_span": { - "path": "clippy_lints/src/transmute.rs", - "line": 222 + "path": "src/transmute/mod.rs", + "line": 227 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for transmutes from an integer to a float.\n **Why is this bad?** Transmutes are dangerous and error-prone, whereas `from_bits` is intuitive\n and safe.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n unsafe {\n let _: f32 = std::mem::transmute(1_u32); // where x: u32\n }\n\n // should be:\n let _: f32 = f32::from_bits(1_u32);\n ```\n", @@ -4813,8 +4881,8 @@ { "id": "transmute_float_to_int", "id_span": { - "path": "clippy_lints/src/transmute.rs", - "line": 244 + "path": "src/transmute/mod.rs", + "line": 249 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for transmutes from a float to an integer.\n **Why is this bad?** Transmutes are dangerous and error-prone, whereas `to_bits` is intuitive\n and safe.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n unsafe {\n let _: u32 = std::mem::transmute(1f32);\n }\n\n // should be:\n let _: u32 = 1f32.to_bits();\n ```\n", @@ -4826,8 +4894,8 @@ { "id": "transmute_ptr_to_ptr", "id_span": { - "path": "clippy_lints/src/transmute.rs", - "line": 271 + "path": "src/transmute/mod.rs", + "line": 276 }, "group": "clippy::complexity", "docs": " **What it does:** Checks for transmutes from a pointer to a pointer, or from a reference to a reference.\n\n **Why is this bad?** Transmutes are dangerous, and these can instead be\n written as casts.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let ptr = &1u32 as *const u32;\n unsafe {\n // pointer-to-pointer transmute\n let _: *const f32 = std::mem::transmute(ptr);\n // ref-ref transmute\n let _: &f32 = std::mem::transmute(&1u32);\n }\n // These can be respectively written:\n let _ = ptr as *const f32;\n let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) };\n ```\n", @@ -4839,8 +4907,8 @@ { "id": "unsound_collection_transmute", "id_span": { - "path": "clippy_lints/src/transmute.rs", - "line": 299 + "path": "src/transmute/mod.rs", + "line": 304 }, "group": "clippy::correctness", "docs": " **What it does:** Checks for transmutes between collections whose types have different ABI, size or alignment.\n\n **Why is this bad?** This is undefined behavior.\n\n **Known problems:** Currently, we cannot know whether a type is a\n collection, so we just lint the ones that come with `std`.\n\n **Example:**\n ```rust\n // different size, therefore likely out-of-bounds memory access\n // You absolutely do not want this in your code!\n unsafe {\n std::mem::transmute::<_, Vec>(vec![2_u16])\n };\n ```\n\n You must always iterate, map and collect the values:\n\n ```rust\n vec![2_u16].into_iter().map(u32::from).collect::>();\n ```\n", @@ -4852,7 +4920,7 @@ { "id": "transmuting_null", "id_span": { - "path": "clippy_lints/src/transmuting_null.rs", + "path": "src/transmuting_null.rs", "line": 23 }, "group": "clippy::correctness", @@ -4865,7 +4933,7 @@ { "id": "try_err", "id_span": { - "path": "clippy_lints/src/try_err.rs", + "path": "src/try_err.rs", "line": 43 }, "group": "clippy::style", @@ -4878,7 +4946,7 @@ { "id": "box_vec", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 66 }, "group": "clippy::perf", @@ -4891,7 +4959,7 @@ { "id": "vec_box", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 95 }, "group": "clippy::complexity", @@ -4904,7 +4972,7 @@ { "id": "option_option", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 133 }, "group": "clippy::pedantic", @@ -4917,7 +4985,7 @@ { "id": "linkedlist", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 169 }, "group": "clippy::pedantic", @@ -4930,7 +4998,7 @@ { "id": "borrowed_box", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 193 }, "group": "clippy::complexity", @@ -4943,7 +5011,7 @@ { "id": "redundant_allocation", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 217 }, "group": "clippy::perf", @@ -4956,7 +5024,7 @@ { "id": "rc_buffer", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 248 }, "group": "clippy::restriction", @@ -4969,7 +5037,7 @@ { "id": "let_unit_value", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 768 }, "group": "clippy::pedantic", @@ -4982,7 +5050,7 @@ { "id": "unit_cmp", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 849 }, "group": "clippy::correctness", @@ -4995,7 +5063,7 @@ { "id": "unit_arg", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 922 }, "group": "clippy::complexity", @@ -5008,7 +5076,7 @@ { "id": "cast_precision_loss", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 1158 }, "group": "clippy::pedantic", @@ -5021,7 +5089,7 @@ { "id": "cast_sign_loss", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 1179 }, "group": "clippy::pedantic", @@ -5034,7 +5102,7 @@ { "id": "cast_possible_truncation", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 1201 }, "group": "clippy::pedantic", @@ -5047,7 +5115,7 @@ { "id": "cast_possible_wrap", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 1224 }, "group": "clippy::pedantic", @@ -5060,7 +5128,7 @@ { "id": "cast_lossless", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 1256 }, "group": "clippy::pedantic", @@ -5073,7 +5141,7 @@ { "id": "unnecessary_cast", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 1281 }, "group": "clippy::complexity", @@ -5086,7 +5154,7 @@ { "id": "cast_ptr_alignment", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 1305 }, "group": "clippy::pedantic", @@ -5099,7 +5167,7 @@ { "id": "fn_to_numeric_cast", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 1332 }, "group": "clippy::style", @@ -5112,7 +5180,7 @@ { "id": "fn_to_numeric_cast_with_truncation", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 1362 }, "group": "clippy::style", @@ -5125,7 +5193,7 @@ { "id": "type_complexity", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 1897 }, "group": "clippy::complexity", @@ -5138,7 +5206,7 @@ { "id": "char_lit_as_u8", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 2068 }, "group": "clippy::complexity", @@ -5151,7 +5219,7 @@ { "id": "absurd_extreme_comparisons", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 2133 }, "group": "clippy::correctness", @@ -5164,7 +5232,7 @@ { "id": "invalid_upcast_comparisons", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 2294 }, "group": "clippy::pedantic", @@ -5177,7 +5245,7 @@ { "id": "implicit_hasher", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 2515 }, "group": "clippy::pedantic", @@ -5190,7 +5258,7 @@ { "id": "cast_ref_to_mut", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 2866 }, "group": "clippy::correctness", @@ -5203,7 +5271,7 @@ { "id": "ptr_as_ptr", "id_span": { - "path": "clippy_lints/src/types.rs", + "path": "src/types.rs", "line": 2922 }, "group": "clippy::pedantic", @@ -5216,7 +5284,7 @@ { "id": "undropped_manually_drops", "id_span": { - "path": "clippy_lints/src/undropped_manually_drops.rs", + "path": "src/undropped_manually_drops.rs", "line": 27 }, "group": "clippy::correctness", @@ -5229,7 +5297,7 @@ { "id": "invisible_characters", "id_span": { - "path": "clippy_lints/src/unicode.rs", + "path": "src/unicode.rs", "line": 20 }, "group": "clippy::correctness", @@ -5242,7 +5310,7 @@ { "id": "non_ascii_literal", "id_span": { - "path": "clippy_lints/src/unicode.rs", + "path": "src/unicode.rs", "line": 44 }, "group": "clippy::pedantic", @@ -5255,7 +5323,7 @@ { "id": "unicode_not_nfc", "id_span": { - "path": "clippy_lints/src/unicode.rs", + "path": "src/unicode.rs", "line": 61 }, "group": "clippy::pedantic", @@ -5268,7 +5336,7 @@ { "id": "unit_return_expecting_ord", "id_span": { - "path": "clippy_lints/src/unit_return_expecting_ord.rs", + "path": "src/unit_return_expecting_ord.rs", "line": 30 }, "group": "clippy::correctness", @@ -5281,7 +5349,7 @@ { "id": "fn_address_comparisons", "id_span": { - "path": "clippy_lints/src/unnamed_address.rs", + "path": "src/unnamed_address.rs", "line": 27 }, "group": "clippy::correctness", @@ -5294,7 +5362,7 @@ { "id": "vtable_address_comparisons", "id_span": { - "path": "clippy_lints/src/unnamed_address.rs", + "path": "src/unnamed_address.rs", "line": 51 }, "group": "clippy::correctness", @@ -5307,7 +5375,7 @@ { "id": "unnecessary_sort_by", "id_span": { - "path": "clippy_lints/src/unnecessary_sort_by.rs", + "path": "src/unnecessary_sort_by.rs", "line": 41 }, "group": "clippy::complexity", @@ -5320,7 +5388,7 @@ { "id": "unnecessary_wraps", "id_span": { - "path": "clippy_lints/src/unnecessary_wraps.rs", + "path": "src/unnecessary_wraps.rs", "line": 50 }, "group": "clippy::pedantic", @@ -5333,7 +5401,7 @@ { "id": "unnested_or_patterns", "id_span": { - "path": "clippy_lints/src/unnested_or_patterns.rs", + "path": "src/unnested_or_patterns.rs", "line": 47 }, "group": "clippy::pedantic", @@ -5346,7 +5414,7 @@ { "id": "unsafe_removed_from_name", "id_span": { - "path": "clippy_lints/src/unsafe_removed_from_name.rs", + "path": "src/unsafe_removed_from_name.rs", "line": 24 }, "group": "clippy::style", @@ -5359,7 +5427,7 @@ { "id": "unused_io_amount", "id_span": { - "path": "clippy_lints/src/unused_io_amount.rs", + "path": "src/unused_io_amount.rs", "line": 28 }, "group": "clippy::correctness", @@ -5372,7 +5440,7 @@ { "id": "unused_self", "id_span": { - "path": "clippy_lints/src/unused_self.rs", + "path": "src/unused_self.rs", "line": 33 }, "group": "clippy::pedantic", @@ -5385,7 +5453,7 @@ { "id": "unused_unit", "id_span": { - "path": "clippy_lints/src/unused_unit.rs", + "path": "src/unused_unit.rs", "line": 27 }, "group": "clippy::style", @@ -5398,7 +5466,7 @@ { "id": "unnecessary_unwrap", "id_span": { - "path": "clippy_lints/src/unwrap.rs", + "path": "src/unwrap.rs", "line": 40 }, "group": "clippy::complexity", @@ -5411,7 +5479,7 @@ { "id": "panicking_unwrap", "id_span": { - "path": "clippy_lints/src/unwrap.rs", + "path": "src/unwrap.rs", "line": 63 }, "group": "clippy::correctness", @@ -5424,7 +5492,7 @@ { "id": "unwrap_in_result", "id_span": { - "path": "clippy_lints/src/unwrap_in_result.rs", + "path": "src/unwrap_in_result.rs", "line": 47 }, "group": "clippy::restriction", @@ -5437,7 +5505,7 @@ { "id": "upper_case_acronyms", "id_span": { - "path": "clippy_lints/src/upper_case_acronyms.rs", + "path": "src/upper_case_acronyms.rs", "line": 35 }, "group": "clippy::style", @@ -5450,7 +5518,7 @@ { "id": "use_self", "id_span": { - "path": "clippy_lints/src/use_self.rs", + "path": "src/use_self.rs", "line": 53 }, "group": "clippy::nursery", @@ -5463,7 +5531,7 @@ { "id": "useless_conversion", "id_span": { - "path": "clippy_lints/src/useless_conversion.rs", + "path": "src/useless_conversion.rs", "line": 32 }, "group": "clippy::complexity", @@ -5476,7 +5544,7 @@ { "id": "useless_vec", "id_span": { - "path": "clippy_lints/src/vec.rs", + "path": "src/vec.rs", "line": 36 }, "group": "clippy::perf", @@ -5489,7 +5557,7 @@ { "id": "vec_init_then_push", "id_span": { - "path": "clippy_lints/src/vec_init_then_push.rs", + "path": "src/vec_init_then_push.rs", "line": 32 }, "group": "clippy::perf", @@ -5502,7 +5570,7 @@ { "id": "vec_resize_to_zero", "id_span": { - "path": "clippy_lints/src/vec_resize_to_zero.rs", + "path": "src/vec_resize_to_zero.rs", "line": 24 }, "group": "clippy::correctness", @@ -5515,7 +5583,7 @@ { "id": "verbose_file_reads", "id_span": { - "path": "clippy_lints/src/verbose_file_reads.rs", + "path": "src/verbose_file_reads.rs", "line": 29 }, "group": "clippy::restriction", @@ -5528,7 +5596,7 @@ { "id": "wildcard_dependencies", "id_span": { - "path": "clippy_lints/src/wildcard_dependencies.rs", + "path": "src/wildcard_dependencies.rs", "line": 24 }, "group": "clippy::cargo", @@ -5541,27 +5609,33 @@ { "id": "enum_glob_use", "id_span": { - "path": "clippy_lints/src/wildcard_imports.rs", + "path": "src/wildcard_imports.rs", "line": 32 }, "group": "clippy::pedantic", "docs": " **What it does:** Checks for `use Enum::*`.\n **Why is this bad?** It is usually better style to use the prefixed name of\n an enumeration variant, rather than importing variants.\n\n **Known problems:** Old-style enumerations that prefix the variants are\n still around.\n\n **Example:**\n ```rust,ignore\n // Bad\n use std::cmp::Ordering::*;\n foo(Less);\n\n // Good\n use std::cmp::Ordering;\n foo(Ordering::Less)\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } }, { "id": "wildcard_imports", "id_span": { - "path": "clippy_lints/src/wildcard_imports.rs", + "path": "src/wildcard_imports.rs", "line": 83 }, "group": "clippy::pedantic", "docs": " **What it does:** Checks for wildcard imports `use _::*`.\n **Why is this bad?** wildcard imports can pollute the namespace. This is especially bad if\n you try to import something through a wildcard, that already has been imported by name from\n a different source:\n\n ```rust,ignore\n use crate1::foo; // Imports a function named foo\n use crate2::*; // Has a function named foo\n\n foo(); // Calls crate1::foo\n ```\n\n This can lead to confusing error messages at best and to unexpected behavior at worst.\n\n **Exceptions:**\n\n Wildcard imports are allowed from modules named `prelude`. Many crates (including the standard library)\n provide modules named \"prelude\" specifically designed for wildcard import.\n\n `use super::*` is allowed in test modules. This is defined as any module with \"test\" in the name.\n\n These exceptions can be disabled using the `warn-on-all-wildcard-imports` configuration flag.\n\n **Known problems:** If macros are imported through the wildcard, this macro is not included\n by the suggestion and has to be added by hand.\n\n Applying the suggestion when explicit imports of the things imported with a glob import\n exist, may result in `unused_imports` warnings.\n\n **Example:**\n\n ```rust,ignore\n // Bad\n use crate1::*;\n\n foo();\n ```\n\n ```rust,ignore\n // Good\n use crate1::foo;\n\n foo();\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": "MachineApplicable" + } }, { "id": "println_empty_string", "id_span": { - "path": "clippy_lints/src/write.rs", + "path": "src/write.rs", "line": 33 }, "group": "clippy::style", @@ -5574,7 +5648,7 @@ { "id": "print_with_newline", "id_span": { - "path": "clippy_lints/src/write.rs", + "path": "src/write.rs", "line": 57 }, "group": "clippy::style", @@ -5587,7 +5661,7 @@ { "id": "print_stdout", "id_span": { - "path": "clippy_lints/src/write.rs", + "path": "src/write.rs", "line": 75 }, "group": "clippy::restriction", @@ -5600,7 +5674,7 @@ { "id": "print_stderr", "id_span": { - "path": "clippy_lints/src/write.rs", + "path": "src/write.rs", "line": 93 }, "group": "clippy::restriction", @@ -5613,7 +5687,7 @@ { "id": "use_debug", "id_span": { - "path": "clippy_lints/src/write.rs", + "path": "src/write.rs", "line": 110 }, "group": "clippy::restriction", @@ -5626,17 +5700,20 @@ { "id": "print_literal", "id_span": { - "path": "clippy_lints/src/write.rs", + "path": "src/write.rs", "line": 133 }, "group": "clippy::style", "docs": " **What it does:** This lint warns about the use of literals as `print!`/`println!` args.\n **Why is this bad?** Using literals as `println!` args is inefficient\n (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary\n (i.e., just put the literal in the format string)\n\n **Known problems:** Will also warn with macro calls as arguments that expand to literals\n -- e.g., `println!(\"{}\", env!(\"FOO\"))`.\n\n **Example:**\n ```rust\n println!(\"{}\", \"foo\");\n ```\n use the literal without formatting:\n ```rust\n println!(\"foo\");\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } }, { "id": "writeln_empty_string", "id_span": { - "path": "clippy_lints/src/write.rs", + "path": "src/write.rs", "line": 156 }, "group": "clippy::style", @@ -5649,7 +5726,7 @@ { "id": "write_with_newline", "id_span": { - "path": "clippy_lints/src/write.rs", + "path": "src/write.rs", "line": 182 }, "group": "clippy::style", @@ -5662,17 +5739,20 @@ { "id": "write_literal", "id_span": { - "path": "clippy_lints/src/write.rs", + "path": "src/write.rs", "line": 207 }, "group": "clippy::style", "docs": " **What it does:** This lint warns about the use of literals as `write!`/`writeln!` args.\n **Why is this bad?** Using literals as `writeln!` args is inefficient\n (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary\n (i.e., just put the literal in the format string)\n\n **Known problems:** Will also warn with macro calls as arguments that expand to literals\n -- e.g., `writeln!(buf, \"{}\", env!(\"FOO\"))`.\n\n **Example:**\n ```rust\n # use std::fmt::Write;\n # let mut buf = String::new();\n // Bad\n writeln!(buf, \"{}\", \"foo\");\n\n // Good\n writeln!(buf, \"foo\");\n ```\n", - "applicability": null + "applicability": { + "is_multi_suggestion": false, + "applicability": null + } }, { "id": "zero_divided_by_zero", "id_span": { - "path": "clippy_lints/src/zero_div_zero.rs", + "path": "src/zero_div_zero.rs", "line": 23 }, "group": "clippy::complexity", @@ -5685,7 +5765,7 @@ { "id": "zero_sized_map_values", "id_span": { - "path": "clippy_lints/src/zero_sized_map_values.rs", + "path": "src/zero_sized_map_values.rs", "line": 37 }, "group": "clippy::pedantic", From 4fc960301bcd4370bfc2238415c7ba3d2d8f9312 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Wed, 10 Mar 2021 23:04:12 +0100 Subject: [PATCH 57/67] Metadata collection: Rounding up the implementation --- .gitignore | 2 +- .../internal_lints/metadata_collector.rs | 152 +- clippy_utils/src/paths.rs | 6 +- metadata_collection.json | 5778 ----------------- tests/dogfood.rs | 4 - .../track_applicability_value.rs | 50 - 6 files changed, 88 insertions(+), 5904 deletions(-) delete mode 100644 metadata_collection.json delete mode 100644 tests/ui-internal/metadata-collector/track_applicability_value.rs diff --git a/.gitignore b/.gitignore index 565327a7f839c..523bab1882836 100644 --- a/.gitignore +++ b/.gitignore @@ -29,7 +29,7 @@ out # gh pages docs util/gh-pages/lints.json -# **/metadata_collection.json +**/metadata_collection.json # rustfmt backups *.rs.bk diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 0425689b0ddd9..37e90bf8686f4 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -7,34 +7,21 @@ //! 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) -//! -//! The metadata currently contains: -//! - [x] TODO The lint declaration line for [#1303](https://github.com/rust-lang/rust-clippy/issues/1303) -//! and [#6492](https://github.com/rust-lang/rust-clippy/issues/6492) -//! - [ ] TODO The Applicability for each lint for [#4310](https://github.com/rust-lang/rust-clippy/issues/4310) - -// # Applicability -// - TODO xFrednet 2021-01-17: Find lint emit and collect applicability -// - TODO xFrednet 2021-02-28: 4x weird emission forwarding -// - See clippy_lints/src/enum_variants.rs@EnumVariantNames::check_name -// - TODO xFrednet 2021-02-28: 6x emission forwarding with local that is initializes from -// function. -// - See clippy_lints/src/methods/mod.rs@lint_binary_expr_with_method_call -// - TODO xFrednet 2021-02-28: 2x lint from local from function call -// - See clippy_lints/src/misc.rs@check_binary -// - TODO xFrednet 2021-02-28: 2x lint from local from method call -// - See clippy_lints/src/non_copy_const.rs@lint + // # NITs // - TODO xFrednet 2021-02-13: Collect depreciations and maybe renames use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::{self as hir, intravisit, intravisit::Visitor, ExprKind, Item, ItemKind, Mutability, QPath, def::DefKind}; +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::Serialize; +use serde::{ser::SerializeStruct, Serialize, Serializer}; +use std::collections::BinaryHeap; use std::fs::{self, OpenOptions}; use std::io::prelude::*; use std::path::Path; @@ -47,7 +34,7 @@ use crate::utils::{ /// This is the output file of the lint collector. const OUTPUT_FILE: &str = "../metadata_collection.json"; /// These lints are excluded from the export. -const BLACK_LISTED_LINTS: [&str; 2] = ["lint_author", "deep_code_inspection"]; +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"]; @@ -107,7 +94,7 @@ declare_clippy_lint! { /// } /// ``` pub INTERNAL_METADATA_COLLECTOR, - internal, + internal_warn, "A busy bee collection metadata about lints" } @@ -116,7 +103,10 @@ impl_lint_pass!(MetadataCollector => [INTERNAL_METADATA_COLLECTOR]); #[allow(clippy::module_name_repetitions)] #[derive(Debug, Clone, Default)] pub struct MetadataCollector { - lints: Vec, + /// All collected lints + /// + /// We use a Heap here to have the lints added in alphabetic order in the export + lints: BinaryHeap, applicability_into: FxHashMap, } @@ -124,6 +114,8 @@ 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; } @@ -131,7 +123,8 @@ impl Drop for MetadataCollector { let mut applicability_info = std::mem::take(&mut self.applicability_into); // Mapping the final data - self.lints + let mut lints = std::mem::take(&mut self.lints).into_sorted_vec(); + lints .iter_mut() .for_each(|x| x.applicability = applicability_info.remove(&x.id)); @@ -140,11 +133,11 @@ impl Drop for MetadataCollector { 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(&self.lints).unwrap()).unwrap(); + writeln!(file, "{}", serde_json::to_string_pretty(&lints).unwrap()).unwrap(); } } -#[derive(Debug, Clone, Serialize)] +#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)] struct LintMetadata { id: String, id_span: SerializableSpan, @@ -155,6 +148,24 @@ struct LintMetadata { applicability: Option, } +// impl Ord for LintMetadata { +// fn cmp(&self, other: &Self) -> Ordering { +// self.id.cmp(&other.id) +// } +// } +// +// impl PartialOrd for LintMetadata { +// fn partial_cmp(&self, other: &Self) -> Option { +// Some(self.cmp(other)) +// } +// } +// +// impl PartialEq for LintMetadata { +// fn eq(&self, other: &Self) -> bool { +// self.id == other.id +// } +// } + impl LintMetadata { fn new(id: String, id_span: SerializableSpan, group: String, docs: String) -> Self { Self { @@ -167,7 +178,7 @@ impl LintMetadata { } } -#[derive(Debug, Clone, Serialize)] +#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)] struct SerializableSpan { path: String, line: usize, @@ -194,25 +205,30 @@ impl SerializableSpan { } } -#[derive(Debug, Clone, Default, Serialize)] +#[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_suggestion: bool, - applicability: Option, -} - -#[allow(dead_code)] -fn log_to_file(msg: &str) { - let mut file = OpenOptions::new() - .write(true) - .append(true) - .create(true) - .open("metadata-lint.log") - .unwrap(); - - write!(file, "{}", msg).unwrap(); + is_multi_part_suggestion: bool, + applicability: Option, +} + +impl Serialize for ApplicabilityInfo { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let index = self.applicability.unwrap_or_default(); + + let mut s = serializer.serialize_struct("ApplicabilityInfo", 2)?; + s.serialize_field("is_multi_part_suggestion", &self.is_multi_part_suggestion)?; + s.serialize_field( + "applicability", + &paths::APPLICABILITY_VALUES[index][APPLICABILITY_NAME_INDEX], + )?; + s.end() + } } impl<'hir> LateLintPass<'hir> for MetadataCollector { @@ -266,16 +282,20 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { /// ``` 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_complex_emission_info(cx, args); + let mut emission_info = extract_emission_info(cx, args); if emission_info.is_empty() { - lint_collection_error_span(cx, expr.span, "Look, here ... I have no clue what todo with it"); + // 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 it self. return; } for (lint_name, applicability, is_multi_part) in emission_info.drain(..) { let app_info = self.applicability_into.entry(lint_name).or_default(); app_info.applicability = applicability; - app_info.is_multi_suggestion = is_multi_part; + app_info.is_multi_part_suggestion = is_multi_part; } } } @@ -289,7 +309,7 @@ fn sym_to_string(sym: Symbol) -> String { } fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option { - extract_attr_docs(item).or_else(|| { + extract_attr_docs(cx, item).or_else(|| { lint_collection_error_item(cx, item, "could not collect the lint documentation"); None }) @@ -305,8 +325,10 @@ fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option) -> Option { - item.attrs +fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option { + cx.tcx + .hir() + .attrs(item.hir_id()) .iter() .filter_map(|ref x| x.doc_str().map(|sym| sym.as_str().to_string())) .reduce(|mut acc, sym| { @@ -357,15 +379,6 @@ fn lint_collection_error_item(cx: &LateContext<'_>, item: &Item<'_>, message: &s ); } -fn lint_collection_error_span(cx: &LateContext<'_>, span: Span, message: &str) { - span_lint( - cx, - INTERNAL_METADATA_COLLECTOR, - span, - &format!("Metadata collection error: {}", message), - ); -} - // ================================================================== // Applicability // ================================================================== @@ -377,11 +390,15 @@ fn match_lint_emission<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) .find_map(|emission_fn| match_function_call(cx, expr, emission_fn)) } -fn extract_complex_emission_info<'hir>( +fn take_higher_applicability(a: Option, b: Option) -> Option { + 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, bool)> { - let mut lints= Vec::new(); +) -> Vec<(String, Option, bool)> { + let mut lints = Vec::new(); let mut applicability = None; let mut multi_part = false; @@ -401,7 +418,10 @@ fn extract_complex_emission_info<'hir>( } } - lints.drain(..).map(|lint_name| (lint_name, applicability.clone(), multi_part)).collect() + lints + .drain(..) + .map(|lint_name| (lint_name, applicability, multi_part)) + .collect() } /// Resolves the possible lints that this expression could reference @@ -412,7 +432,7 @@ fn resolve_lints(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Vec, expr: &'hir hir::Expr<'hir>) -> Option { +fn resolve_applicability(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option { let mut resolver = ApplicabilityResolver::new(cx); resolver.visit_expr(expr); resolver.complete() @@ -457,7 +477,7 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for LintResolver<'a, '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 { @@ -492,15 +512,11 @@ impl<'a, 'hir> ApplicabilityResolver<'a, 'hir> { } fn add_new_index(&mut self, new_index: usize) { - self.applicability_index = self - .applicability_index - .map_or(new_index, |old_index| old_index.min(new_index)) - .into(); + self.applicability_index = take_higher_applicability(self.applicability_index, Some(new_index)); } - fn complete(self) -> Option { + fn complete(self) -> Option { self.applicability_index - .map(|index| paths::APPLICABILITY_VALUES[index][APPLICABILITY_NAME_INDEX].to_string()) } } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 1f19724ad250b..7c7bb9b02b111 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -9,10 +9,10 @@ pub const ANY_TRAIT: [&str; 3] = ["core", "any", "Any"]; pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"]; #[cfg(feature = "metadata-collector-lint")] pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [ - ["rustc_lint_defs", "Applicability", "MachineApplicable"], - ["rustc_lint_defs", "Applicability", "MaybeIncorrect"], - ["rustc_lint_defs", "Applicability", "HasPlaceholders"], ["rustc_lint_defs", "Applicability", "Unspecified"], + ["rustc_lint_defs", "Applicability", "HasPlaceholders"], + ["rustc_lint_defs", "Applicability", "MaybeIncorrect"], + ["rustc_lint_defs", "Applicability", "MachineApplicable"], ]; #[cfg(feature = "metadata-collector-lint")] pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"]; diff --git a/metadata_collection.json b/metadata_collection.json deleted file mode 100644 index f6f72ffa5fa9c..0000000000000 --- a/metadata_collection.json +++ /dev/null @@ -1,5778 +0,0 @@ -[ - { - "id": "approx_constant", - "id_span": { - "path": "src/approx_const.rs", - "line": 34 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for floating point literals that approximate constants which are defined in\n [`std::f32::consts`](https://doc.rust-lang.org/stable/std/f32/consts/#constants)\n or\n [`std::f64::consts`](https://doc.rust-lang.org/stable/std/f64/consts/#constants),\n respectively, suggesting to use the predefined constant.\n\n **Why is this bad?** Usually, the definition in the standard library is more\n precise than what people come up with. If you find that your definition is\n actually more precise, please [file a Rust\n issue](https://github.com/rust-lang/rust/issues).\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = 3.14;\n let y = 1_f64 / x;\n ```\n Use predefined constants instead:\n ```rust\n let x = std::f32::consts::PI;\n let y = std::f64::consts::FRAC_1_PI;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "integer_arithmetic", - "id_span": { - "path": "src/arithmetic.rs", - "line": 28 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for integer arithmetic operations which could overflow or panic.\n Specifically, checks for any operators (`+`, `-`, `*`, `<<`, etc) which are capable\n of overflowing according to the [Rust\n Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow),\n or which can panic (`/`, `%`). No bounds analysis or sophisticated reasoning is\n attempted.\n\n **Why is this bad?** Integer overflow will trigger a panic in debug builds or will wrap in\n release mode. Division by zero will cause a panic in either mode. In some applications one\n wants explicitly checked, wrapping or saturating arithmetic.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let a = 0;\n a + 1;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "float_arithmetic", - "id_span": { - "path": "src/arithmetic.rs", - "line": 46 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for float arithmetic.\n **Why is this bad?** For some embedded systems or kernel development, it\n can be useful to rule out floating-point numbers.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let a = 0.0;\n a + 1.0;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "as_conversions", - "id_span": { - "path": "src/as_conversions.rs", - "line": 42 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of `as` conversions.\n Note that this lint is specialized in linting *every single* use of `as`\n regardless of whether good alternatives exist or not.\n If you want more precise lints for `as`, please consider using these separate lints:\n `unnecessary_cast`, `cast_lossless/possible_truncation/possible_wrap/precision_loss/sign_loss`,\n `fn_to_numeric_cast(_with_truncation)`, `char_lit_as_u8`, `ref_to_mut` and `ptr_as_ptr`.\n There is a good explanation the reason why this lint should work in this way and how it is useful\n [in this issue](https://github.com/rust-lang/rust-clippy/issues/5122).\n\n **Why is this bad?** `as` conversions will perform many kinds of\n conversions, including silently lossy conversions and dangerous coercions.\n There are cases when it makes sense to use `as`, so the lint is\n Allow by default.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n let a: u32;\n ...\n f(a as u16);\n ```\n\n Usually better represents the semantics you expect:\n ```rust,ignore\n f(a.try_into()?);\n ```\n or\n ```rust,ignore\n f(a.try_into().expect(\"Unexpected u16 overflow in f\"));\n ```\n\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "inline_asm_x86_intel_syntax", - "id_span": { - "path": "src/asm_syntax.rs", - "line": 78 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of Intel x86 assembly syntax.\n **Why is this bad?** The lint has been enabled to indicate a preference\n for AT&T x86 assembly syntax.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,no_run\n # #![feature(asm)]\n # unsafe { let ptr = \"\".as_ptr();\n asm!(\"lea {}, [{}]\", lateout(reg) _, in(reg) ptr);\n # }\n ```\n Use instead:\n ```rust,no_run\n # #![feature(asm)]\n # unsafe { let ptr = \"\".as_ptr();\n asm!(\"lea ({}), {}\", in(reg) ptr, lateout(reg) _, options(att_syntax));\n # }\n ```\n", - "applicability": null - }, - { - "id": "inline_asm_x86_att_syntax", - "id_span": { - "path": "src/asm_syntax.rs", - "line": 114 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of AT&T x86 assembly syntax.\n **Why is this bad?** The lint has been enabled to indicate a preference\n for Intel x86 assembly syntax.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,no_run\n # #![feature(asm)]\n # unsafe { let ptr = \"\".as_ptr();\n asm!(\"lea ({}), {}\", in(reg) ptr, lateout(reg) _, options(att_syntax));\n # }\n ```\n Use instead:\n ```rust,no_run\n # #![feature(asm)]\n # unsafe { let ptr = \"\".as_ptr();\n asm!(\"lea {}, [{}]\", lateout(reg) _, in(reg) ptr);\n # }\n ```\n", - "applicability": null - }, - { - "id": "assertions_on_constants", - "id_span": { - "path": "src/assertions_on_constants.rs", - "line": 23 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `assert!(true)` and `assert!(false)` calls.\n **Why is this bad?** Will be optimized out by the compiler or should probably be replaced by a\n `panic!()` or `unreachable!()`\n\n **Known problems:** None\n\n **Example:**\n ```rust,ignore\n assert!(false)\n assert!(true)\n const B: bool = false;\n assert!(B)\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "assign_op_pattern", - "id_span": { - "path": "src/assign_ops.rs", - "line": 33 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `a = a op b` or `a = b commutative_op a` patterns.\n\n **Why is this bad?** These can be written as the shorter `a op= b`.\n\n **Known problems:** While forbidden by the spec, `OpAssign` traits may have\n implementations that differ from the regular `Op` impl.\n\n **Example:**\n ```rust\n let mut a = 5;\n let b = 0;\n // ...\n // Bad\n a = a + b;\n\n // Good\n a += b;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "misrefactored_assign_op", - "id_span": { - "path": "src/assign_ops.rs", - "line": 56 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for `a op= a op b` or `a op= b op a` patterns.\n **Why is this bad?** Most likely these are bugs where one meant to write `a\n op= b`.\n\n **Known problems:** Clippy cannot know for sure if `a op= a op b` should have\n been `a = a op a op b` or `a = a op b`/`a op= b`. Therefore, it suggests both.\n If `a op= a op b` is really the correct behaviour it should be\n written as `a = a op a op b` as it's less confusing.\n\n **Example:**\n ```rust\n let mut a = 5;\n let b = 2;\n // ...\n a += a + b;\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "async_yields_async", - "id_span": { - "path": "src/async_yields_async.rs", - "line": 36 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for async blocks that yield values of types that can themselves be awaited.\n\n **Why is this bad?** An await is likely missing.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n async fn foo() {}\n\n fn bar() {\n let x = async {\n foo()\n };\n }\n ```\n Use instead:\n ```rust\n async fn foo() {}\n\n fn bar() {\n let x = async {\n foo().await\n };\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "invalid_atomic_ordering", - "id_span": { - "path": "src/atomic_ordering.rs", - "line": 47 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for usage of invalid atomic ordering in atomic loads/stores/exchanges/updates and\n memory fences.\n\n **Why is this bad?** Using an invalid atomic ordering\n will cause a panic at run-time.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,no_run\n # use std::sync::atomic::{self, AtomicU8, Ordering};\n\n let x = AtomicU8::new(0);\n\n // Bad: `Release` and `AcqRel` cannot be used for `load`.\n let _ = x.load(Ordering::Release);\n let _ = x.load(Ordering::AcqRel);\n\n // Bad: `Acquire` and `AcqRel` cannot be used for `store`.\n x.store(1, Ordering::Acquire);\n x.store(2, Ordering::AcqRel);\n\n // Bad: `Relaxed` cannot be used as a fence's ordering.\n atomic::fence(Ordering::Relaxed);\n atomic::compiler_fence(Ordering::Relaxed);\n\n // Bad: `Release` and `AcqRel` are both always invalid\n // for the failure ordering (the last arg).\n let _ = x.compare_exchange(1, 2, Ordering::SeqCst, Ordering::Release);\n let _ = x.compare_exchange_weak(2, 3, Ordering::AcqRel, Ordering::AcqRel);\n\n // Bad: The failure ordering is not allowed to be\n // stronger than the success order, and `SeqCst` is\n // stronger than `Relaxed`.\n let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |val| Some(val + val));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "inline_always", - "id_span": { - "path": "src/attrs.rs", - "line": 65 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for items annotated with `#[inline(always)]`, unless the annotated function is empty or simply panics.\n\n **Why is this bad?** While there are valid uses of this annotation (and once\n you know when to use it, by all means `allow` this lint), it's a common\n newbie-mistake to pepper one's code with it.\n\n As a rule of thumb, before slapping `#[inline(always)]` on a function,\n measure if that additional function call really affects your runtime profile\n sufficiently to make up for the increase in compile time.\n\n **Known problems:** False positives, big time. This lint is meant to be\n deactivated by everyone doing serious performance work. This means having\n done the measurement.\n\n **Example:**\n ```ignore\n #[inline(always)]\n fn not_quite_hot_code(..) { ... }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "useless_attribute", - "id_span": { - "path": "src/attrs.rs", - "line": 99 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `extern crate` and `use` items annotated with lint attributes.\n\n This lint permits `#[allow(unused_imports)]`, `#[allow(deprecated)]`,\n `#[allow(unreachable_pub)]`, `#[allow(clippy::wildcard_imports)]` and\n `#[allow(clippy::enum_glob_use)]` on `use` items and `#[allow(unused_imports)]` on\n `extern crate` items with a `#[macro_use]` attribute.\n\n **Why is this bad?** Lint attributes have no effect on crate imports. Most\n likely a `!` was forgotten.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n // Bad\n #[deny(dead_code)]\n extern crate foo;\n #[forbid(dead_code)]\n use foo::bar;\n\n // Ok\n #[allow(unused_imports)]\n use foo::baz;\n #[allow(unused_imports)]\n #[macro_use]\n extern crate baz;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "deprecated_semver", - "id_span": { - "path": "src/attrs.rs", - "line": 118 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `#[deprecated]` annotations with a `since` field that is not a valid semantic version.\n\n **Why is this bad?** For checking the version of the deprecation, it must be\n a valid semver. Failing that, the contained information is useless.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n #[deprecated(since = \"forever\")]\n fn something_else() { /* ... */ }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "empty_line_after_outer_attr", - "id_span": { - "path": "src/attrs.rs", - "line": 153 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for empty lines after outer attributes\n **Why is this bad?**\n Most likely the attribute was meant to be an inner attribute using a '!'.\n If it was meant to be an outer attribute, then the following item\n should not be separated by empty lines.\n\n **Known problems:** Can cause false positives.\n\n From the clippy side it's difficult to detect empty lines between an attributes and the\n following item because empty lines and comments are not part of the AST. The parsing\n currently works for basic cases but is not perfect.\n\n **Example:**\n ```rust\n // Good (as inner attribute)\n #![allow(dead_code)]\n\n fn this_is_fine() { }\n\n // Bad\n #[allow(dead_code)]\n\n fn not_quite_good_code() { }\n\n // Good (as outer attribute)\n #[allow(dead_code)]\n fn this_is_fine_too() { }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "blanket_clippy_restriction_lints", - "id_span": { - "path": "src/attrs.rs", - "line": 176 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category.\n **Why is this bad?** Restriction lints sometimes are in contrast with other lints or even go against idiomatic rust.\n These lints should only be enabled on a lint-by-lint basis and with careful consideration.\n\n **Known problems:** None.\n\n **Example:**\n Bad:\n ```rust\n #![deny(clippy::restriction)]\n ```\n\n Good:\n ```rust\n #![deny(clippy::as_conversions)]\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "deprecated_cfg_attr", - "id_span": { - "path": "src/attrs.rs", - "line": 205 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it with `#[rustfmt::skip]`.\n\n **Why is this bad?** Since tool_attributes ([rust-lang/rust#44690](https://github.com/rust-lang/rust/issues/44690))\n are stable now, they should be used instead of the old `cfg_attr(rustfmt)` attributes.\n\n **Known problems:** This lint doesn't detect crate level inner attributes, because they get\n processed before the PreExpansionPass lints get executed. See\n [#3123](https://github.com/rust-lang/rust-clippy/pull/3123#issuecomment-422321765)\n\n **Example:**\n\n Bad:\n ```rust\n #[cfg_attr(rustfmt, rustfmt_skip)]\n fn main() { }\n ```\n\n Good:\n ```rust\n #[rustfmt::skip]\n fn main() { }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "mismatched_target_os", - "id_span": { - "path": "src/attrs.rs", - "line": 238 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for cfg attributes having operating systems used in target family position.\n **Why is this bad?** The configuration option will not be recognised and the related item will not be included\n by the conditional compilation engine.\n\n **Known problems:** None.\n\n **Example:**\n\n Bad:\n ```rust\n #[cfg(linux)]\n fn conditional() { }\n ```\n\n Good:\n ```rust\n #[cfg(target_os = \"linux\")]\n fn conditional() { }\n ```\n\n Or:\n ```rust\n #[cfg(unix)]\n fn conditional() { }\n ```\n Check the [Rust Reference](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os) for more details.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "await_holding_lock", - "id_span": { - "path": "src/await_holding_invalid.rs", - "line": 47 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for calls to await while holding a non-async-aware MutexGuard.\n\n **Why is this bad?** The Mutex types found in std::sync and parking_lot\n are not designed to operate in an async context across await points.\n\n There are two potential solutions. One is to use an asynx-aware Mutex\n type. Many asynchronous foundation crates provide such a Mutex type. The\n other solution is to ensure the mutex is unlocked before calling await,\n either by introducing a scope or an explicit call to Drop::drop.\n\n **Known problems:** Will report false positive for explicitly dropped guards ([#6446](https://github.com/rust-lang/rust-clippy/issues/6446)).\n\n **Example:**\n\n ```rust,ignore\n use std::sync::Mutex;\n\n async fn foo(x: &Mutex) {\n let guard = x.lock().unwrap();\n *guard += 1;\n bar.await;\n }\n ```\n\n Use instead:\n ```rust,ignore\n use std::sync::Mutex;\n\n async fn foo(x: &Mutex) {\n {\n let guard = x.lock().unwrap();\n *guard += 1;\n }\n bar.await;\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "await_holding_refcell_ref", - "id_span": { - "path": "src/await_holding_invalid.rs", - "line": 86 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for calls to await while holding a `RefCell` `Ref` or `RefMut`.\n\n **Why is this bad?** `RefCell` refs only check for exclusive mutable access\n at runtime. Holding onto a `RefCell` ref across an `await` suspension point\n risks panics from a mutable ref shared while other refs are outstanding.\n\n **Known problems:** Will report false positive for explicitly dropped refs ([#6353](https://github.com/rust-lang/rust-clippy/issues/6353)).\n\n **Example:**\n\n ```rust,ignore\n use std::cell::RefCell;\n\n async fn foo(x: &RefCell) {\n let mut y = x.borrow_mut();\n *y += 1;\n bar.await;\n }\n ```\n\n Use instead:\n ```rust,ignore\n use std::cell::RefCell;\n\n async fn foo(x: &RefCell) {\n {\n let mut y = x.borrow_mut();\n *y += 1;\n }\n bar.await;\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "bad_bit_mask", - "id_span": { - "path": "src/bit_mask.rs", - "line": 44 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for incompatible bit masks in comparisons.\n The formula for detecting if an expression of the type `_ m\n c` (where `` is one of {`&`, `|`} and `` is one of\n {`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following\n table:\n\n |Comparison |Bit Op|Example |is always|Formula |\n |------------|------|------------|---------|----------------------|\n |`==` or `!=`| `&` |`x & 2 == 3`|`false` |`c & m != c` |\n |`<` or `>=`| `&` |`x & 2 < 3` |`true` |`m < c` |\n |`>` or `<=`| `&` |`x & 1 > 1` |`false` |`m <= c` |\n |`==` or `!=`| `|` |`x | 1 == 0`|`false` |`c | m != c` |\n |`<` or `>=`| `|` |`x | 1 < 1` |`false` |`m >= c` |\n |`<=` or `>` | `|` |`x | 1 > 0` |`true` |`m > c` |\n\n **Why is this bad?** If the bits that the comparison cares about are always\n set to zero or one by the bit mask, the comparison is constant `true` or\n `false` (depending on mask, compared value, and operators).\n\n So the code is actively misleading, and the only reason someone would write\n this intentionally is to win an underhanded Rust contest or create a\n test-case for this lint.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = 1;\n if (x & 1 == 2) { }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "ineffective_bit_mask", - "id_span": { - "path": "src/bit_mask.rs", - "line": 73 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for bit masks in comparisons which can be removed without changing the outcome. The basic structure can be seen in the\n following table:\n\n |Comparison| Bit Op |Example |equals |\n |----------|---------|-----------|-------|\n |`>` / `<=`|`|` / `^`|`x | 2 > 3`|`x > 3`|\n |`<` / `>=`|`|` / `^`|`x ^ 1 < 4`|`x < 4`|\n\n **Why is this bad?** Not equally evil as [`bad_bit_mask`](#bad_bit_mask),\n but still a bit misleading, because the bit mask is ineffective.\n\n **Known problems:** False negatives: This lint will only match instances\n where we have figured out the math (which is for a power-of-two compared\n value). This means things like `x | 1 >= 7` (which would be better written\n as `x >= 6`) will not be reported (but bit masks like this are fairly\n uncommon).\n\n **Example:**\n ```rust\n # let x = 1;\n if (x | 1 > 3) { }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "verbose_bit_mask", - "id_span": { - "path": "src/bit_mask.rs", - "line": 92 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for bit masks that can be replaced by a call to `trailing_zeros`\n\n **Why is this bad?** `x.trailing_zeros() > 4` is much clearer than `x & 15\n == 0`\n\n **Known problems:** llvm generates better code for `x & 15 == 0` on x86\n\n **Example:**\n ```rust\n # let x = 1;\n if x & 0b1111 == 0 { }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "blacklisted_name", - "id_span": { - "path": "src/blacklisted_name.rs", - "line": 20 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usage of blacklisted names for variables, such as `foo`.\n\n **Why is this bad?** These names are usually placeholder names and should be\n avoided.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let foo = 3.14;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "blocks_in_if_conditions", - "id_span": { - "path": "src/blocks_in_if_conditions.rs", - "line": 42 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `if` conditions that use blocks containing an expression, statements or conditions that use closures with blocks.\n\n **Why is this bad?** Style, using blocks in the condition makes it hard to read.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n // Bad\n if { true } { /* ... */ }\n\n // Good\n if true { /* ... */ }\n ```\n\n // or\n\n ```rust\n # fn somefunc() -> bool { true };\n // Bad\n if { let x = somefunc(); x } { /* ... */ }\n\n // Good\n let res = { let x = somefunc(); x };\n if res { /* ... */ }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "nonminimal_bool", - "id_span": { - "path": "src/booleans.rs", - "line": 31 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for boolean expressions that can be written more concisely.\n\n **Why is this bad?** Readability of boolean expressions suffers from\n unnecessary duplication.\n\n **Known problems:** Ignores short circuiting behavior of `||` and\n `&&`. Ignores `|`, `&` and `^`.\n\n **Example:**\n ```ignore\n if a && true // should be: if a\n if !(a == b) // should be: if a != b\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "logic_bug", - "id_span": { - "path": "src/booleans.rs", - "line": 49 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for boolean expressions that contain terminals that can be eliminated.\n\n **Why is this bad?** This is most likely a logic bug.\n\n **Known problems:** Ignores short circuiting behavior.\n\n **Example:**\n ```ignore\n if a && b || a { ... }\n ```\n The `b` is unnecessary, the expression is equivalent to `if a`.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "naive_bytecount", - "id_span": { - "path": "src/bytecount.rs", - "line": 30 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for naive byte counts\n **Why is this bad?** The [`bytecount`](https://crates.io/crates/bytecount)\n crate has methods to count your bytes faster, especially for large slices.\n\n **Known problems:** If you have predominantly small slices, the\n `bytecount::count(..)` method may actually be slower. However, if you can\n ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be\n faster in those cases.\n\n **Example:**\n\n ```rust\n # let vec = vec![1_u8];\n &vec.iter().filter(|x| **x == 0u8).count(); // use bytecount::count instead\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "cargo_common_metadata", - "id_span": { - "path": "src/cargo_common_metadata.rs", - "line": 49 - }, - "group": "clippy::cargo", - "docs": " **What it does:** Checks to see if all common metadata is defined in `Cargo.toml`. See: https://rust-lang-nursery.github.io/api-guidelines/documentation.html#cargotoml-includes-all-common-metadata-c-metadata\n\n **Why is this bad?** It will be more difficult for users to discover the\n purpose of the crate, and key information related to it.\n\n **Known problems:** None.\n\n **Example:**\n ```toml\n # This `Cargo.toml` is missing an authors field:\n [package]\n name = \"clippy\"\n version = \"0.0.212\"\n description = \"A bunch of helpful lints to avoid common pitfalls in Rust\"\n repository = \"https://github.com/rust-lang/rust-clippy\"\n readme = \"README.md\"\n license = \"MIT OR Apache-2.0\"\n keywords = [\"clippy\", \"lint\", \"plugin\"]\n categories = [\"development-tools\", \"development-tools::cargo-plugins\"]\n ```\n\n Should include an authors field like:\n\n ```toml\n # This `Cargo.toml` includes all common metadata\n [package]\n name = \"clippy\"\n version = \"0.0.212\"\n authors = [\"Someone \"]\n description = \"A bunch of helpful lints to avoid common pitfalls in Rust\"\n repository = \"https://github.com/rust-lang/rust-clippy\"\n readme = \"README.md\"\n license = \"MIT OR Apache-2.0\"\n keywords = [\"clippy\", \"lint\", \"plugin\"]\n categories = [\"development-tools\", \"development-tools::cargo-plugins\"]\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "case_sensitive_file_extension_comparisons", - "id_span": { - "path": "src/case_sensitive_file_extension_comparisons.rs", - "line": 34 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for calls to `ends_with` with possible file extensions\n and suggests to use a case-insensitive approach instead.\n\n **Why is this bad?**\n `ends_with` is case-sensitive and may not detect files with a valid extension.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n fn is_rust_file(filename: &str) -> bool {\n filename.ends_with(\".rs\")\n }\n ```\n Use instead:\n ```rust\n fn is_rust_file(filename: &str) -> bool {\n filename.rsplit('.').next().map(|ext| ext.eq_ignore_ascii_case(\"rs\")) == Some(true)\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "checked_conversions", - "id_span": { - "path": "src/checked_conversions.rs", - "line": 40 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for explicit bounds checking when casting.\n **Why is this bad?** Reduces the readability of statements & is error prone.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let foo: u32 = 5;\n # let _ =\n foo <= i32::MAX as u32\n # ;\n ```\n\n Could be written:\n\n ```rust\n # use std::convert::TryFrom;\n # let foo = 1;\n # let _ =\n i32::try_from(foo).is_ok()\n # ;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "cognitive_complexity", - "id_span": { - "path": "src/cognitive_complexity.rs", - "line": 24 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for methods with high cognitive complexity.\n **Why is this bad?** Methods of high cognitive complexity tend to be hard to\n both read and maintain. Also LLVM will tend to optimize small methods better.\n\n **Known problems:** Sometimes it's hard to find a way to reduce the\n complexity.\n\n **Example:** No. You'll see it when you get the warning.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "collapsible_if", - "id_span": { - "path": "src/collapsible_if.rs", - "line": 50 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for nested `if` statements which can be collapsed by `&&`-combining their conditions.\n\n **Why is this bad?** Each `if`-statement adds one level of nesting, which\n makes code look more complex than it really is.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n if x {\n if y {\n …\n }\n }\n\n ```\n\n Should be written:\n\n ```rust.ignore\n if x && y {\n …\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "collapsible_else_if", - "id_span": { - "path": "src/collapsible_if.rs", - "line": 85 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for collapsible `else { if ... }` expressions that can be collapsed to `else if ...`.\n\n **Why is this bad?** Each `if`-statement adds one level of nesting, which\n makes code look more complex than it really is.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n\n if x {\n …\n } else {\n if y {\n …\n }\n }\n ```\n\n Should be written:\n\n ```rust.ignore\n if x {\n …\n } else if y {\n …\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "collapsible_match", - "id_span": { - "path": "src/collapsible_match.rs", - "line": 44 - }, - "group": "clippy::style", - "docs": " **What it does:** Finds nested `match` or `if let` expressions where the patterns may be \"collapsed\" together without adding any branches.\n\n Note that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only\n cases where merging would most likely make the code more readable.\n\n **Why is this bad?** It is unnecessarily verbose and complex.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n fn func(opt: Option>) {\n let n = match opt {\n Some(n) => match n {\n Ok(n) => n,\n _ => return,\n }\n None => return,\n };\n }\n ```\n Use instead:\n ```rust\n fn func(opt: Option>) {\n let n = match opt {\n Some(Ok(n)) => n,\n _ => return,\n };\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "comparison_chain", - "id_span": { - "path": "src/comparison_chain.rs", - "line": 49 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks comparison chains written with `if` that can be rewritten with `match` and `cmp`.\n\n **Why is this bad?** `if` is not guaranteed to be exhaustive and conditionals can get\n repetitive\n\n **Known problems:** The match statement may be slower due to the compiler\n not inlining the call to cmp. See issue [#5354](https://github.com/rust-lang/rust-clippy/issues/5354)\n\n **Example:**\n ```rust,ignore\n # fn a() {}\n # fn b() {}\n # fn c() {}\n fn f(x: u8, y: u8) {\n if x > y {\n a()\n } else if x < y {\n b()\n } else {\n c()\n }\n }\n ```\n\n Could be written:\n\n ```rust,ignore\n use std::cmp::Ordering;\n # fn a() {}\n # fn b() {}\n # fn c() {}\n fn f(x: u8, y: u8) {\n match x.cmp(&y) {\n Ordering::Greater => a(),\n Ordering::Less => b(),\n Ordering::Equal => c()\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "ifs_same_cond", - "id_span": { - "path": "src/copies.rs", - "line": 33 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for consecutive `if`s with the same condition.\n **Why is this bad?** This is probably a copy & paste error.\n\n **Known problems:** Hopefully none.\n\n **Example:**\n ```ignore\n if a == b {\n …\n } else if a == b {\n …\n }\n ```\n\n Note that this lint ignores all conditions with a function call as it could\n have side effects:\n\n ```ignore\n if foo() {\n …\n } else if foo() { // not linted\n …\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "same_functions_in_if_condition", - "id_span": { - "path": "src/copies.rs", - "line": 80 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for consecutive `if`s with the same function call.\n **Why is this bad?** This is probably a copy & paste error.\n Despite the fact that function can have side effects and `if` works as\n intended, such an approach is implicit and can be considered a \"code smell\".\n\n **Known problems:** Hopefully none.\n\n **Example:**\n ```ignore\n if foo() == bar {\n …\n } else if foo() == bar {\n …\n }\n ```\n\n This probably should be:\n ```ignore\n if foo() == bar {\n …\n } else if foo() == baz {\n …\n }\n ```\n\n or if the original code was not a typo and called function mutates a state,\n consider move the mutation out of the `if` condition to avoid similarity to\n a copy & paste error:\n\n ```ignore\n let first = foo();\n if first == bar {\n …\n } else {\n let second = foo();\n if second == bar {\n …\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "if_same_then_else", - "id_span": { - "path": "src/copies.rs", - "line": 101 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `if/else` with the same body as the *then* part and the *else* part.\n\n **Why is this bad?** This is probably a copy & paste error.\n\n **Known problems:** Hopefully none.\n\n **Example:**\n ```ignore\n let foo = if … {\n 42\n } else {\n 42\n };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "copy_iterator", - "id_span": { - "path": "src/copy_iterator.rs", - "line": 27 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for types that implement `Copy` as well as `Iterator`.\n\n **Why is this bad?** Implicit copies can be confusing when working with\n iterator combinators.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n #[derive(Copy, Clone)]\n struct Countdown(u8);\n\n impl Iterator for Countdown {\n // ...\n }\n\n let a: Vec<_> = my_iterator.take(1).collect();\n let b: Vec<_> = my_iterator.collect();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "create_dir", - "id_span": { - "path": "src/create_dir.rs", - "line": 24 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks usage of `std::fs::create_dir` and suggest using `std::fs::create_dir_all` instead.\n **Why is this bad?** Sometimes `std::fs::create_dir` is mistakenly chosen over `std::fs::create_dir_all`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n std::fs::create_dir(\"foo\");\n ```\n Use instead:\n ```rust\n std::fs::create_dir_all(\"foo\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "dbg_macro", - "id_span": { - "path": "src/dbg_macro.rs", - "line": 25 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of dbg!() macro.\n **Why is this bad?** `dbg!` macro is intended as a debugging tool. It\n should not be in version control.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n // Bad\n dbg!(true)\n\n // Good\n true\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "default_trait_access", - "id_span": { - "path": "src/default.rs", - "line": 33 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for literal calls to `Default::default()`.\n **Why is this bad?** It's more clear to the reader to use the name of the type whose default is\n being gotten than the generic `Default`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n let s: String = Default::default();\n\n // Good\n let s = String::default();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "field_reassign_with_default", - "id_span": { - "path": "src/default.rs", - "line": 63 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for immediate reassignment of fields initialized with Default::default().\n\n **Why is this bad?**It's more idiomatic to use the [functional update syntax](https://doc.rust-lang.org/reference/expressions/struct-expr.html#functional-update-syntax).\n\n **Known problems:** Assignments to patterns that are of tuple type are not linted.\n\n **Example:**\n Bad:\n ```\n # #[derive(Default)]\n # struct A { i: i32 }\n let mut a: A = Default::default();\n a.i = 42;\n ```\n Use instead:\n ```\n # #[derive(Default)]\n # struct A { i: i32 }\n let a = A {\n i: 42,\n .. Default::default()\n };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "default_numeric_fallback", - "id_span": { - "path": "src/default_numeric_fallback.rs", - "line": 44 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of unconstrained numeric literals which may cause default numeric fallback in type inference.\n\n Default numeric fallback means that if numeric types have not yet been bound to concrete\n types at the end of type inference, then integer type is bound to `i32`, and similarly\n floating type is bound to `f64`.\n\n See [RFC0212](https://github.com/rust-lang/rfcs/blob/master/text/0212-restore-int-fallback.md) for more information about the fallback.\n\n **Why is this bad?** For those who are very careful about types, default numeric fallback\n can be a pitfall that cause unexpected runtime behavior.\n\n **Known problems:** This lint can only be allowed at the function level or above.\n\n **Example:**\n ```rust\n let i = 10;\n let f = 1.23;\n ```\n\n Use instead:\n ```rust\n let i = 10i32;\n let f = 1.23f64;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "explicit_deref_methods", - "id_span": { - "path": "src/dereference.rs", - "line": 32 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for explicit `deref()` or `deref_mut()` method calls.\n **Why is this bad?** Dereferencing by `&*x` or `&mut *x` is clearer and more concise,\n when not part of a method chain.\n\n **Example:**\n ```rust\n use std::ops::Deref;\n let a: &mut String = &mut String::from(\"foo\");\n let b: &str = a.deref();\n ```\n Could be written as:\n ```rust\n let a: &mut String = &mut String::from(\"foo\");\n let b = &*a;\n ```\n\n This lint excludes\n ```rust,ignore\n let _ = d.unwrap().deref();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "derive_hash_xor_eq", - "id_span": { - "path": "src/derive.rs", - "line": 42 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for deriving `Hash` but implementing `PartialEq` explicitly or vice versa.\n\n **Why is this bad?** The implementation of these traits must agree (for\n example for use with `HashMap`) so it’s probably a bad idea to use a\n default-generated `Hash` implementation with an explicitly defined\n `PartialEq`. In particular, the following must hold for any type:\n\n ```text\n k1 == k2 ⇒ hash(k1) == hash(k2)\n ```\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n #[derive(Hash)]\n struct Foo;\n\n impl PartialEq for Foo {\n ...\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "derive_ord_xor_partial_ord", - "id_span": { - "path": "src/derive.rs", - "line": 93 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for deriving `Ord` but implementing `PartialOrd` explicitly or vice versa.\n\n **Why is this bad?** The implementation of these traits must agree (for\n example for use with `sort`) so it’s probably a bad idea to use a\n default-generated `Ord` implementation with an explicitly defined\n `PartialOrd`. In particular, the following must hold for any type\n implementing `Ord`:\n\n ```text\n k1.cmp(&k2) == k1.partial_cmp(&k2).unwrap()\n ```\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,ignore\n #[derive(Ord, PartialEq, Eq)]\n struct Foo;\n\n impl PartialOrd for Foo {\n ...\n }\n ```\n Use instead:\n ```rust,ignore\n #[derive(PartialEq, Eq)]\n struct Foo;\n\n impl PartialOrd for Foo {\n fn partial_cmp(&self, other: &Foo) -> Option {\n Some(self.cmp(other))\n }\n }\n\n impl Ord for Foo {\n ...\n }\n ```\n or, if you don't need a custom ordering:\n ```rust,ignore\n #[derive(Ord, PartialOrd, PartialEq, Eq)]\n struct Foo;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "expl_impl_clone_on_copy", - "id_span": { - "path": "src/derive.rs", - "line": 119 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for explicit `Clone` implementations for `Copy` types.\n\n **Why is this bad?** To avoid surprising behaviour, these traits should\n agree and the behaviour of `Copy` cannot be overridden. In almost all\n situations a `Copy` type should have a `Clone` implementation that does\n nothing more than copy the object, which is what `#[derive(Copy, Clone)]`\n gets you.\n\n **Known problems:** Bounds of generic types are sometimes wrong: https://github.com/rust-lang/rust/issues/26925\n\n **Example:**\n ```rust,ignore\n #[derive(Copy)]\n struct Foo;\n\n impl Clone for Foo {\n // ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unsafe_derive_deserialize", - "id_span": { - "path": "src/derive.rs", - "line": 153 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for deriving `serde::Deserialize` on a type that has methods using `unsafe`.\n\n **Why is this bad?** Deriving `serde::Deserialize` will create a constructor\n that may violate invariants hold by another constructor.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,ignore\n use serde::Deserialize;\n\n #[derive(Deserialize)]\n pub struct Foo {\n // ..\n }\n\n impl Foo {\n pub fn new() -> Self {\n // setup here ..\n }\n\n pub unsafe fn parts() -> (&str, &str) {\n // assumes invariants hold\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "disallowed_method", - "id_span": { - "path": "src/disallowed_method.rs", - "line": 46 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Denies the configured methods and functions in clippy.toml\n **Why is this bad?** Some methods are undesirable in certain contexts,\n and it's beneficial to lint for them as needed.\n\n **Known problems:** Currently, you must write each function as a\n fully-qualified path. This lint doesn't support aliases or reexported\n names; be aware that many types in `std` are actually reexports.\n\n For example, if you want to disallow `Duration::as_secs`, your clippy.toml\n configuration would look like\n `disallowed-methods = [\"core::time::Duration::as_secs\"]` and not\n `disallowed-methods = [\"std::time::Duration::as_secs\"]` as you might expect.\n\n **Example:**\n\n An example clippy.toml configuration:\n ```toml\n # clippy.toml\n disallowed-methods = [\"alloc::vec::Vec::leak\", \"std::time::Instant::now\"]\n ```\n\n ```rust,ignore\n // Example code where clippy issues a warning\n let xs = vec![1, 2, 3, 4];\n xs.leak(); // Vec::leak is disallowed in the config.\n\n let _now = Instant::now(); // Instant::now is disallowed in the config.\n ```\n\n Use instead:\n ```rust,ignore\n // Example code which does not raise clippy warning\n let mut xs = Vec::new(); // Vec::new is _not_ disallowed in the config.\n xs.push(123); // Vec::push is _not_ disallowed in the config.\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "doc_markdown", - "id_span": { - "path": "src/doc.rs", - "line": 63 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for the presence of `_`, `::` or camel-case words outside ticks in documentation.\n\n **Why is this bad?** *Rustdoc* supports markdown formatting, `_`, `::` and\n camel-case probably indicates some code which should be included between\n ticks. `_` can also be used for emphasis in markdown, this lint tries to\n consider that.\n\n **Known problems:** Lots of bad docs won’t be fixed, what the lint checks\n for is limited, and there are still false positives.\n\n In addition, when writing documentation comments, including `[]` brackets\n inside a link text would trip the parser. Therfore, documenting link with\n `[`SmallVec<[T; INLINE_CAPACITY]>`]` and then [`SmallVec<[T; INLINE_CAPACITY]>`]: SmallVec\n would fail.\n\n **Examples:**\n ```rust\n /// Do something with the foo_bar parameter. See also\n /// that::other::module::foo.\n // ^ `foo_bar` and `that::other::module::foo` should be ticked.\n fn doit(foo_bar: usize) {}\n ```\n\n ```rust\n // Link text with `[]` brackets should be written as following:\n /// Consume the array and return the inner\n /// [`SmallVec<[T; INLINE_CAPACITY]>`][SmallVec].\n /// [SmallVec]: SmallVec\n fn main() {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "missing_safety_doc", - "id_span": { - "path": "src/doc.rs", - "line": 97 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for the doc comments of publicly visible unsafe functions and warns if there is no `# Safety` section.\n\n **Why is this bad?** Unsafe functions should document their safety\n preconditions, so that users can be sure they are using them safely.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n# type Universe = ();\n /// This function should really be documented\n pub unsafe fn start_apocalypse(u: &mut Universe) {\n unimplemented!();\n }\n ```\n\n At least write a line about safety:\n\n ```rust\n# type Universe = ();\n /// # Safety\n ///\n /// This function should not be called before the horsemen are ready.\n pub unsafe fn start_apocalypse(u: &mut Universe) {\n unimplemented!();\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "missing_errors_doc", - "id_span": { - "path": "src/doc.rs", - "line": 126 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks the doc comments of publicly visible functions that return a `Result` type and warns if there is no `# Errors` section.\n\n **Why is this bad?** Documenting the type of errors that can be returned from a\n function can help callers write code to handle the errors appropriately.\n\n **Known problems:** None.\n\n **Examples:**\n\n Since the following function returns a `Result` it has an `# Errors` section in\n its doc comment:\n\n ```rust\n# use std::io;\n /// # Errors\n ///\n /// Will return `Err` if `filename` does not exist or the user does not have\n /// permission to read it.\n pub fn read(filename: String) -> io::Result {\n unimplemented!();\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "missing_panics_doc", - "id_span": { - "path": "src/doc.rs", - "line": 157 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks the doc comments of publicly visible functions that may panic and warns if there is no `# Panics` section.\n\n **Why is this bad?** Documenting the scenarios in which panicking occurs\n can help callers who do not want to panic to avoid those situations.\n\n **Known problems:** None.\n\n **Examples:**\n\n Since the following function may panic it has a `# Panics` section in\n its doc comment:\n\n ```rust\n /// # Panics\n ///\n /// Will panic if y is 0\n pub fn divide_by(x: i32, y: i32) -> i32 {\n if y == 0 {\n panic!(\"Cannot divide by 0\")\n } else {\n x / y\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "needless_doctest_main", - "id_span": { - "path": "src/doc.rs", - "line": 185 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `fn main() { .. }` in doctests\n **Why is this bad?** The test can be shorter (and likely more readable)\n if the `fn main()` is left implicit.\n\n **Known problems:** None.\n\n **Examples:**\n ``````rust\n /// An example of a doctest with a `main()` function\n ///\n /// # Examples\n ///\n /// ```\n /// fn main() {\n /// // this needs not be in an `fn`\n /// }\n /// ```\n fn needless_main() {\n unimplemented!();\n }\n ``````\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "double_comparisons", - "id_span": { - "path": "src/double_comparison.rs", - "line": 33 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for double comparisons that could be simplified to a single expression.\n\n **Why is this bad?** Readability.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = 1;\n # let y = 2;\n if x == y || x < y {}\n ```\n\n Could be written as:\n\n ```rust\n # let x = 1;\n # let y = 2;\n if x <= y {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "double_parens", - "id_span": { - "path": "src/double_parens.rs", - "line": 35 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for unnecessary double parentheses.\n **Why is this bad?** This makes code harder to read and might indicate a\n mistake.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n fn simple_double_parens() -> i32 {\n ((0))\n }\n\n // Good\n fn simple_no_parens() -> i32 {\n 0\n }\n\n // or\n\n # fn foo(bar: usize) {}\n // Bad\n foo((0));\n\n // Good\n foo(0);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "drop_ref", - "id_span": { - "path": "src/drop_forget_ref.rs", - "line": 26 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for calls to `std::mem::drop` with a reference instead of an owned value.\n\n **Why is this bad?** Calling `drop` on a reference will only drop the\n reference itself, which is a no-op. It will not call the `drop` method (from\n the `Drop` trait implementation) on the underlying referenced value, which\n is likely what was intended.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n let mut lock_guard = mutex.lock();\n std::mem::drop(&lock_guard) // Should have been drop(lock_guard), mutex\n // still locked\n operation_that_requires_mutex_to_be_unlocked();\n ```\n", - "applicability": null - }, - { - "id": "forget_ref", - "id_span": { - "path": "src/drop_forget_ref.rs", - "line": 47 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for calls to `std::mem::forget` with a reference instead of an owned value.\n\n **Why is this bad?** Calling `forget` on a reference will only forget the\n reference itself, which is a no-op. It will not forget the underlying\n referenced\n value, which is likely what was intended.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = Box::new(1);\n std::mem::forget(&x) // Should have been forget(x), x will still be dropped\n ```\n", - "applicability": null - }, - { - "id": "drop_copy", - "id_span": { - "path": "src/drop_forget_ref.rs", - "line": 68 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for calls to `std::mem::drop` with a value that derives the Copy trait\n\n **Why is this bad?** Calling `std::mem::drop` [does nothing for types that\n implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html), since the\n value will be copied and moved into the function on invocation.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x: i32 = 42; // i32 implements Copy\n std::mem::drop(x) // A copy of x is passed to the function, leaving the\n // original unaffected\n ```\n", - "applicability": null - }, - { - "id": "forget_copy", - "id_span": { - "path": "src/drop_forget_ref.rs", - "line": 95 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for calls to `std::mem::forget` with a value that derives the Copy trait\n\n **Why is this bad?** Calling `std::mem::forget` [does nothing for types that\n implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html) since the\n value will be copied and moved into the function on invocation.\n\n An alternative, but also valid, explanation is that Copy types do not\n implement\n the Drop trait, which means they have no destructors. Without a destructor,\n there\n is nothing for `std::mem::forget` to ignore.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x: i32 = 42; // i32 implements Copy\n std::mem::forget(x) // A copy of x is passed to the function, leaving the\n // original unaffected\n ```\n", - "applicability": null - }, - { - "id": "duration_subsec", - "id_span": { - "path": "src/duration_subsec.rs", - "line": 34 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for calculation of subsecond microseconds or milliseconds from other `Duration` methods.\n\n **Why is this bad?** It's more concise to call `Duration::subsec_micros()` or\n `Duration::subsec_millis()` than to calculate them.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::time::Duration;\n let dur = Duration::new(5, 0);\n\n // Bad\n let _micros = dur.subsec_nanos() / 1_000;\n let _millis = dur.subsec_nanos() / 1_000_000;\n\n // Good\n let _micros = dur.subsec_micros();\n let _millis = dur.subsec_millis();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "else_if_without_else", - "id_span": { - "path": "src/else_if_without_else.rs", - "line": 44 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of if expressions with an `else if` branch, but without a final `else` branch.\n\n **Why is this bad?** Some coding guidelines require this (e.g., MISRA-C:2004 Rule 14.10).\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn a() {}\n # fn b() {}\n # let x: i32 = 1;\n if x.is_positive() {\n a();\n } else if x.is_negative() {\n b();\n }\n ```\n\n Could be written:\n\n ```rust\n # fn a() {}\n # fn b() {}\n # let x: i32 = 1;\n if x.is_positive() {\n a();\n } else if x.is_negative() {\n b();\n } else {\n // We don't care about zero.\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "empty_enum", - "id_span": { - "path": "src/empty_enum.rs", - "line": 38 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for `enum`s with no variants.\n As of this writing, the `never_type` is still a\n nightly-only experimental API. Therefore, this lint is only triggered\n if the `never_type` is enabled.\n\n **Why is this bad?** If you want to introduce a type which\n can't be instantiated, you should use `!` (the primitive type \"never\"),\n or a wrapper around it, because `!` has more extensive\n compiler support (type inference, etc...) and wrappers\n around it are the conventional way to define an uninhabited type.\n For further information visit [never type documentation](https://doc.rust-lang.org/std/primitive.never.html)\n\n\n **Known problems:** None.\n\n **Example:**\n\n Bad:\n ```rust\n enum Test {}\n ```\n\n Good:\n ```rust\n #![feature(never_type)]\n\n struct Test(!);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "map_entry", - "id_span": { - "path": "src/entry.rs", - "line": 48 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for uses of `contains_key` + `insert` on `HashMap` or `BTreeMap`.\n\n **Why is this bad?** Using `entry` is more efficient.\n\n **Known problems:** Some false negatives, eg.:\n ```rust\n # use std::collections::HashMap;\n # let mut map = HashMap::new();\n # let v = 1;\n # let k = 1;\n if !map.contains_key(&k) {\n map.insert(k.clone(), v);\n }\n ```\n\n **Example:**\n ```rust\n # use std::collections::HashMap;\n # let mut map = HashMap::new();\n # let k = 1;\n # let v = 1;\n if !map.contains_key(&k) {\n map.insert(k, v);\n }\n ```\n can both be rewritten as:\n ```rust\n # use std::collections::HashMap;\n # let mut map = HashMap::new();\n # let k = 1;\n # let v = 1;\n map.entry(k).or_insert(v);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "enum_clike_unportable_variant", - "id_span": { - "path": "src/enum_clike.rs", - "line": 31 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for C-like enumerations that are `repr(isize/usize)` and have values that don't fit into an `i32`.\n\n **Why is this bad?** This will truncate the variant value on 32 bit\n architectures, but works fine on 64 bit.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # #[cfg(target_pointer_width = \"64\")]\n #[repr(usize)]\n enum NonPortable {\n X = 0x1_0000_0000,\n Y = 0,\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "enum_variant_names", - "id_span": { - "path": "src/enum_variants.rs", - "line": 36 - }, - "group": "clippy::style", - "docs": " **What it does:** Detects enumeration variants that are prefixed or suffixed by the same characters.\n\n **Why is this bad?** Enumeration variant names should specify their variant,\n not repeat the enumeration name.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n enum Cake {\n BlackForestCake,\n HummingbirdCake,\n BattenbergCake,\n }\n ```\n Could be written as:\n ```rust\n enum Cake {\n BlackForest,\n Hummingbird,\n Battenberg,\n }\n ```\n", - "applicability": null - }, - { - "id": "pub_enum_variant_names", - "id_span": { - "path": "src/enum_variants.rs", - "line": 66 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Detects public enumeration variants that are prefixed or suffixed by the same characters.\n\n **Why is this bad?** Public enumeration variant names should specify their variant,\n not repeat the enumeration name.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n pub enum Cake {\n BlackForestCake,\n HummingbirdCake,\n BattenbergCake,\n }\n ```\n Could be written as:\n ```rust\n pub enum Cake {\n BlackForest,\n Hummingbird,\n Battenberg,\n }\n ```\n", - "applicability": null - }, - { - "id": "module_name_repetitions", - "id_span": { - "path": "src/enum_variants.rs", - "line": 91 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Detects type names that are prefixed or suffixed by the containing module's name.\n\n **Why is this bad?** It requires the user to type the module name twice.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n mod cake {\n struct BlackForestCake;\n }\n ```\n Could be written as:\n ```rust\n mod cake {\n struct BlackForest;\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "module_inception", - "id_span": { - "path": "src/enum_variants.rs", - "line": 121 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for modules that have the same name as their parent module\n\n **Why is this bad?** A typical beginner mistake is to have `mod foo;` and\n again `mod foo { ..\n }` in `foo.rs`.\n The expectation is that items inside the inner `mod foo { .. }` are then\n available\n through `foo::x`, but they are only available through\n `foo::foo::x`.\n If this is done on purpose, it would be better to choose a more\n representative module name.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n // lib.rs\n mod foo;\n // foo.rs\n mod foo {\n ...\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "eq_op", - "id_span": { - "path": "src/eq_op.rs", - "line": 34 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for equal operands to comparison, logical and bitwise, difference and division binary operators (`==`, `>`, etc., `&&`,\n `||`, `&`, `|`, `^`, `-` and `/`).\n\n **Why is this bad?** This is usually just a typo or a copy and paste error.\n\n **Known problems:** False negatives: We had some false positives regarding\n calls (notably [racer](https://github.com/phildawes/racer) had one instance\n of `x.pop() && x.pop()`), so we removed matching any function or method\n calls. We may introduce a list of known pure functions in the future.\n\n **Example:**\n ```rust\n # let x = 1;\n if x + 1 == x + 1 {}\n ```\n or\n ```rust\n # let a = 3;\n # let b = 4;\n assert_eq!(a, a);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "op_ref", - "id_span": { - "path": "src/eq_op.rs", - "line": 56 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for arguments to `==` which have their address taken to satisfy a bound\n and suggests to dereference the other argument instead\n\n **Why is this bad?** It is more idiomatic to dereference the other argument.\n\n **Known problems:** None\n\n **Example:**\n ```ignore\n // Bad\n &x == y\n\n // Good\n x == *y\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "erasing_op", - "id_span": { - "path": "src/erasing_op.rs", - "line": 25 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for erasing operations, e.g., `x * 0`.\n **Why is this bad?** The whole expression can be replaced by zero.\n This is most likely not the intended outcome and should probably be\n corrected\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = 1;\n 0 / x;\n 0 * x;\n x & 0;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "boxed_local", - "id_span": { - "path": "src/escape.rs", - "line": 43 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for usage of `Box` where an unboxed `T` would work fine.\n\n **Why is this bad?** This is an unnecessary allocation, and bad for\n performance. It is only necessary to allocate if you wish to move the box\n into something.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn foo(bar: usize) {}\n // Bad\n let x = Box::new(1);\n foo(*x);\n println!(\"{}\", *x);\n\n // Good\n let x = 1;\n foo(x);\n println!(\"{}\", x);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "redundant_closure", - "id_span": { - "path": "src/eta_reduction.rs", - "line": 37 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for closures which just call another function where the function can be called directly. `unsafe` functions or calls where types\n get adjusted are ignored.\n\n **Why is this bad?** Needlessly creating a closure adds code for no benefit\n and gives the optimizer more work.\n\n **Known problems:** If creating the closure inside the closure has a side-\n effect then moving the closure creation out will change when that side-\n effect runs.\n See [#1439](https://github.com/rust-lang/rust-clippy/issues/1439) for more details.\n\n **Example:**\n ```rust,ignore\n // Bad\n xs.map(|x| foo(x))\n\n // Good\n xs.map(foo)\n ```\n where `foo(_)` is a plain function that takes the exact argument type of\n `x`.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "redundant_closure_for_method_calls", - "id_span": { - "path": "src/eta_reduction.rs", - "line": 61 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for closures which only invoke a method on the closure argument and can be replaced by referencing the method directly.\n\n **Why is this bad?** It's unnecessary to create the closure.\n\n **Known problems:** [#3071](https://github.com/rust-lang/rust-clippy/issues/3071),\n [#3942](https://github.com/rust-lang/rust-clippy/issues/3942),\n [#4002](https://github.com/rust-lang/rust-clippy/issues/4002)\n\n\n **Example:**\n ```rust,ignore\n Some('a').map(|s| s.to_uppercase());\n ```\n may be rewritten as\n ```rust,ignore\n Some('a').map(char::to_uppercase);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "eval_order_dependence", - "id_span": { - "path": "src/eval_order_dependence.rs", - "line": 38 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for a read and a write to the same variable where whether the read occurs before or after the write depends on the evaluation\n order of sub-expressions.\n\n **Why is this bad?** It is often confusing to read. In addition, the\n sub-expression evaluation order for Rust is not well documented.\n\n **Known problems:** Code which intentionally depends on the evaluation\n order, or which is correct for any evaluation order.\n\n **Example:**\n ```rust\n let mut x = 0;\n\n // Bad\n let a = {\n x = 1;\n 1\n } + x;\n // Unclear whether a is 1 or 2.\n\n // Good\n let tmp = {\n x = 1;\n 1\n };\n let a = tmp + x;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "diverging_sub_expression", - "id_span": { - "path": "src/eval_order_dependence.rs", - "line": 62 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for diverging calls that are not match arms or statements.\n\n **Why is this bad?** It is often confusing to read. In addition, the\n sub-expression evaluation order for Rust is not well documented.\n\n **Known problems:** Someone might want to use `some_bool || panic!()` as a\n shorthand.\n\n **Example:**\n ```rust,no_run\n # fn b() -> bool { true }\n # fn c() -> bool { true }\n let a = b() || panic!() || c();\n // `c()` is dead, `panic!()` is only called if `b()` returns `false`\n let x = (a, b, c, panic!());\n // can simply be replaced by `panic!()`\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "struct_excessive_bools", - "id_span": { - "path": "src/excessive_bools.rs", - "line": 40 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for excessive use of bools in structs.\n\n **Why is this bad?** Excessive bools in a struct\n is often a sign that it's used as a state machine,\n which is much better implemented as an enum.\n If it's not the case, excessive bools usually benefit\n from refactoring into two-variant enums for better\n readability and API.\n\n **Known problems:** None.\n\n **Example:**\n Bad:\n ```rust\n struct S {\n is_pending: bool,\n is_processing: bool,\n is_finished: bool,\n }\n ```\n\n Good:\n ```rust\n enum S {\n Pending,\n Processing,\n Finished,\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "fn_params_excessive_bools", - "id_span": { - "path": "src/excessive_bools.rs", - "line": 78 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for excessive use of bools in function definitions.\n\n **Why is this bad?** Calls to such functions\n are confusing and error prone, because it's\n hard to remember argument order and you have\n no type system support to back you up. Using\n two-variant enums instead of bools often makes\n API easier to use.\n\n **Known problems:** None.\n\n **Example:**\n Bad:\n ```rust,ignore\n fn f(is_round: bool, is_hot: bool) { ... }\n ```\n\n Good:\n ```rust,ignore\n enum Shape {\n Round,\n Spiky,\n }\n\n enum Temperature {\n Hot,\n IceCold,\n }\n\n fn f(shape: Shape, temperature: Temperature) { ... }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "exhaustive_enums", - "id_span": { - "path": "src/exhaustive_items.rs", - "line": 34 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Warns on any exported `enum`s that are not tagged `#[non_exhaustive]`\n **Why is this bad?** Exhaustive enums are typically fine, but a project which does\n not wish to make a stability commitment around exported enums may wish to\n disable them by default.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n enum Foo {\n Bar,\n Baz\n }\n ```\n Use instead:\n ```rust\n #[non_exhaustive]\n enum Foo {\n Bar,\n Baz\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "exhaustive_structs", - "id_span": { - "path": "src/exhaustive_items.rs", - "line": 64 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Warns on any exported `structs`s that are not tagged `#[non_exhaustive]`\n **Why is this bad?** Exhaustive structs are typically fine, but a project which does\n not wish to make a stability commitment around exported structs may wish to\n disable them by default.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n struct Foo {\n bar: u8,\n baz: String,\n }\n ```\n Use instead:\n ```rust\n #[non_exhaustive]\n struct Foo {\n bar: u8,\n baz: String,\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "exit", - "id_span": { - "path": "src/exit.rs", - "line": 20 - }, - "group": "clippy::restriction", - "docs": " **What it does:** `exit()` terminates the program and doesn't provide a stack trace.\n\n **Why is this bad?** Ideally a program is terminated by finishing\n the main function.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n std::process::exit(0)\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "explicit_write", - "id_span": { - "path": "src/explicit_write.rs", - "line": 25 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `write!()` / `writeln()!` which can be replaced with `(e)print!()` / `(e)println!()`\n\n **Why is this bad?** Using `(e)println! is clearer and more concise\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::io::Write;\n # let bar = \"furchtbar\";\n // this would be clearer as `eprintln!(\"foo: {:?}\", bar);`\n writeln!(&mut std::io::stderr(), \"foo: {:?}\", bar).unwrap();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "fallible_impl_from", - "id_span": { - "path": "src/fallible_impl_from.rs", - "line": 45 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for impls of `From<..>` that contain `panic!()` or `unwrap()`\n **Why is this bad?** `TryFrom` should be used if there's a possibility of failure.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n struct Foo(i32);\n\n // Bad\n impl From for Foo {\n fn from(s: String) -> Self {\n Foo(s.parse().unwrap())\n }\n }\n ```\n\n ```rust\n // Good\n struct Foo(i32);\n\n use std::convert::TryFrom;\n impl TryFrom for Foo {\n type Error = ();\n fn try_from(s: String) -> Result {\n if let Ok(parsed) = s.parse() {\n Ok(Foo(parsed))\n } else {\n Err(())\n }\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "float_equality_without_abs", - "id_span": { - "path": "src/float_equality_without_abs.rs", - "line": 37 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for statements of the form `(a - b) < f32::EPSILON` or `(a - b) < f64::EPSILON`. Notes the missing `.abs()`.\n\n **Why is this bad?** The code without `.abs()` is more likely to have a bug.\n\n **Known problems:** If the user can ensure that b is larger than a, the `.abs()` is\n technically unneccessary. However, it will make the code more robust and doesn't have any\n large performance implications. If the abs call was deliberately left out for performance\n reasons, it is probably better to state this explicitly in the code, which then can be done\n with an allow.\n\n **Example:**\n\n ```rust\n pub fn is_roughly_equal(a: f32, b: f32) -> bool {\n (a - b) < f32::EPSILON\n }\n ```\n Use instead:\n ```rust\n pub fn is_roughly_equal(a: f32, b: f32) -> bool {\n (a - b).abs() < f32::EPSILON\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "excessive_precision", - "id_span": { - "path": "src/float_literal.rs", - "line": 30 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for float literals with a precision greater than that supported by the underlying type.\n\n **Why is this bad?** Rust will truncate the literal silently.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let v: f32 = 0.123_456_789_9;\n println!(\"{}\", v); // 0.123_456_789\n\n // Good\n let v: f64 = 0.123_456_789_9;\n println!(\"{}\", v); // 0.123_456_789_9\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "lossy_float_literal", - "id_span": { - "path": "src/float_literal.rs", - "line": 54 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for whole number float literals that cannot be represented as the underlying type without loss.\n\n **Why is this bad?** Rust will silently lose precision during\n conversion to a float.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let _: f32 = 16_777_217.0; // 16_777_216.0\n\n // Good\n let _: f32 = 16_777_216.0;\n let _: f64 = 16_777_217.0;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "imprecise_flops", - "id_span": { - "path": "src/floating_point_arithmetic.rs", - "line": 45 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Looks for floating-point expressions that can be expressed using built-in methods to improve accuracy\n at the cost of performance.\n\n **Why is this bad?** Negatively impacts accuracy.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n let a = 3f32;\n let _ = a.powf(1.0 / 3.0);\n let _ = (1.0 + a).ln();\n let _ = a.exp() - 1.0;\n ```\n\n is better expressed as\n\n ```rust\n let a = 3f32;\n let _ = a.cbrt();\n let _ = a.ln_1p();\n let _ = a.exp_m1();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "suboptimal_flops", - "id_span": { - "path": "src/floating_point_arithmetic.rs", - "line": 102 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Looks for floating-point expressions that can be expressed using built-in methods to improve both\n accuracy and performance.\n\n **Why is this bad?** Negatively impacts accuracy and performance.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n use std::f32::consts::E;\n\n let a = 3f32;\n let _ = (2f32).powf(a);\n let _ = E.powf(a);\n let _ = a.powf(1.0 / 2.0);\n let _ = a.log(2.0);\n let _ = a.log(10.0);\n let _ = a.log(E);\n let _ = a.powf(2.0);\n let _ = a * 2.0 + 4.0;\n let _ = if a < 0.0 {\n -a\n } else {\n a\n };\n let _ = if a < 0.0 {\n a\n } else {\n -a\n };\n ```\n\n is better expressed as\n\n ```rust\n use std::f32::consts::E;\n\n let a = 3f32;\n let _ = a.exp2();\n let _ = a.exp();\n let _ = a.sqrt();\n let _ = a.log2();\n let _ = a.log10();\n let _ = a.ln();\n let _ = a.powi(2);\n let _ = a.mul_add(2.0, 4.0);\n let _ = a.abs();\n let _ = -a.abs();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "useless_format", - "id_span": { - "path": "src/format.rs", - "line": 37 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for the use of `format!(\"string literal with no argument\")` and `format!(\"{}\", foo)` where `foo` is a string.\n\n **Why is this bad?** There is no point of doing that. `format!(\"foo\")` can\n be replaced by `\"foo\".to_owned()` if you really need a `String`. The even\n worse `&format!(\"foo\")` is often encountered in the wild. `format!(\"{}\",\n foo)` can be replaced by `foo.clone()` if `foo: String` or `foo.to_owned()`\n if `foo: &str`.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n\n // Bad\n # let foo = \"foo\";\n format!(\"{}\", foo);\n\n // Good\n format!(\"foo\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "suspicious_assignment_formatting", - "id_span": { - "path": "src/formatting.rs", - "line": 22 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for use of the non-existent `=*`, `=!` and `=-` operators.\n\n **Why is this bad?** This is either a typo of `*=`, `!=` or `-=` or\n confusing.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n a =- 42; // confusing, should it be `a -= 42` or `a = -42`?\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "suspicious_unary_op_formatting", - "id_span": { - "path": "src/formatting.rs", - "line": 44 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks the formatting of a unary operator on the right hand side of a binary operator. It lints if there is no space between the binary and unary operators,\n but there is a space between the unary and its operand.\n\n **Why is this bad?** This is either a typo in the binary operator or confusing.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n if foo <- 30 { // this should be `foo < -30` but looks like a different operator\n }\n\n if foo &&! bar { // this should be `foo && !bar` but looks like a different operator\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "suspicious_else_formatting", - "id_span": { - "path": "src/formatting.rs", - "line": 80 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for formatting of `else`. It lints if the `else` is followed immediately by a newline or the `else` seems to be missing.\n\n **Why is this bad?** This is probably some refactoring remnant, even if the\n code is correct, it might look confusing.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n if foo {\n } { // looks like an `else` is missing here\n }\n\n if foo {\n } if bar { // looks like an `else` is missing here\n }\n\n if foo {\n } else\n\n { // this is the `else` block of the previous `if`, but should it be?\n }\n\n if foo {\n } else\n\n if bar { // this is the `else` block of the previous `if`, but should it be?\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "possible_missing_comma", - "id_span": { - "path": "src/formatting.rs", - "line": 100 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for possible missing comma in an array. It lints if an array element is a binary operator expression and it lies on two lines.\n\n **Why is this bad?** This could lead to unexpected results.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n let a = &[\n -1, -2, -3 // <= no comma here\n -4, -5, -6\n ];\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "from_over_into", - "id_span": { - "path": "src/from_over_into.rs", - "line": 39 - }, - "group": "clippy::style", - "docs": " **What it does:** Searches for implementations of the `Into<..>` trait and suggests to implement `From<..>` instead.\n **Why is this bad?** According the std docs implementing `From<..>` is preferred since it gives you `Into<..>` for free where the reverse isn't true.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n struct StringWrapper(String);\n\n impl Into for String {\n fn into(self) -> StringWrapper {\n StringWrapper(self)\n }\n }\n ```\n Use instead:\n ```rust\n struct StringWrapper(String);\n\n impl From for StringWrapper {\n fn from(s: String) -> StringWrapper {\n StringWrapper(s)\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "from_str_radix_10", - "id_span": { - "path": "src/from_str_radix_10.rs", - "line": 37 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for function invocations of the form `primitive::from_str_radix(s, 10)`\n\n **Why is this bad?**\n This specific common use case can be rewritten as `s.parse::()`\n (and in most cases, the turbofish can be removed), which reduces code length\n and complexity.\n\n **Known problems:**\n This lint may suggest using (&).parse() instead of .parse() directly\n in some cases, which is correct but adds unnecessary complexity to the code.\n\n **Example:**\n\n ```ignore\n let input: &str = get_input();\n let num = u16::from_str_radix(input, 10)?;\n ```\n Use instead:\n ```ignore\n let input: &str = get_input();\n let num: u16 = input.parse()?;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "too_many_arguments", - "id_span": { - "path": "src/functions.rs", - "line": 39 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for functions with too many parameters.\n **Why is this bad?** Functions with lots of parameters are considered bad\n style and reduce readability (“what does the 5th parameter mean?”). Consider\n grouping some parameters into a new type.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct Color;\n fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) {\n // ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "too_many_lines", - "id_span": { - "path": "src/functions.rs", - "line": 62 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for functions with a large amount of lines.\n **Why is this bad?** Functions with a lot of lines are harder to understand\n due to having to look at a larger amount of code to understand what the\n function is doing. Consider splitting the body of the function into\n multiple functions.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn im_too_long() {\n println!(\"\");\n // ... 100 more LoC\n println!(\"\");\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "not_unsafe_ptr_arg_deref", - "id_span": { - "path": "src/functions.rs", - "line": 96 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for public functions that dereference raw pointer arguments but are not marked unsafe.\n\n **Why is this bad?** The function should probably be marked `unsafe`, since\n for an arbitrary raw pointer, there is no way of telling for sure if it is\n valid.\n\n **Known problems:**\n\n * It does not check functions recursively so if the pointer is passed to a\n private non-`unsafe` function which does the dereferencing, the lint won't\n trigger.\n * It only checks for arguments whose type are raw pointers, not raw pointers\n got from an argument in some other way (`fn foo(bar: &[*const u8])` or\n `some_argument.get_raw_ptr()`).\n\n **Example:**\n ```rust,ignore\n // Bad\n pub fn foo(x: *const u8) {\n println!(\"{}\", unsafe { *x });\n }\n\n // Good\n pub unsafe fn foo(x: *const u8) {\n println!(\"{}\", unsafe { *x });\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "must_use_unit", - "id_span": { - "path": "src/functions.rs", - "line": 117 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for a [`#[must_use]`] attribute on unit-returning functions and methods.\n\n [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute\n\n **Why is this bad?** Unit values are useless. The attribute is likely\n a remnant of a refactoring that removed the return type.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n #[must_use]\n fn useless() { }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "double_must_use", - "id_span": { - "path": "src/functions.rs", - "line": 142 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for a [`#[must_use]`] attribute without further information on functions and methods that return a type already\n marked as `#[must_use]`.\n\n [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute\n\n **Why is this bad?** The attribute isn't needed. Not using the result\n will already be reported. Alternatively, one can add some text to the\n attribute to improve the lint message.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n #[must_use]\n fn double_must_use() -> Result<(), ()> {\n unimplemented!();\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "must_use_candidate", - "id_span": { - "path": "src/functions.rs", - "line": 170 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for public functions that have no [`#[must_use]`] attribute, but return something not already marked\n must-use, have no mutable arg and mutate no statics.\n\n [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute\n\n **Why is this bad?** Not bad at all, this lint just shows places where\n you could add the attribute.\n\n **Known problems:** The lint only checks the arguments for mutable\n types without looking if they are actually changed. On the other hand,\n it also ignores a broad range of potentially interesting side effects,\n because we cannot decide whether the programmer intends the function to\n be called for the side effect or the result. Expect many false\n positives. At least we don't lint if the result type is unit or already\n `#[must_use]`.\n\n **Examples:**\n ```rust\n // this could be annotated with `#[must_use]`.\n fn id(t: T) -> T { t }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "result_unit_err", - "id_span": { - "path": "src/functions.rs", - "line": 216 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for public functions that return a `Result` with an `Err` type of `()`. It suggests using a custom type that\n implements [`std::error::Error`].\n\n **Why is this bad?** Unit does not implement `Error` and carries no\n further information about what went wrong.\n\n **Known problems:** Of course, this lint assumes that `Result` is used\n for a fallible operation (which is after all the intended use). However\n code may opt to (mis)use it as a basic two-variant-enum. In that case,\n the suggestion is misguided, and the code should use a custom enum\n instead.\n\n **Examples:**\n ```rust\n pub fn read_u8() -> Result { Err(()) }\n ```\n should become\n ```rust,should_panic\n use std::fmt;\n\n #[derive(Debug)]\n pub struct EndOfStream;\n\n impl fmt::Display for EndOfStream {\n fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n write!(f, \"End of Stream\")\n }\n }\n\n impl std::error::Error for EndOfStream { }\n\n pub fn read_u8() -> Result { Err(EndOfStream) }\n# fn main() {\n# read_u8().unwrap();\n# }\n ```\n\n Note that there are crates that simplify creating the error type, e.g.\n [`thiserror`](https://docs.rs/thiserror).\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "future_not_send", - "id_span": { - "path": "src/future_not_send.rs", - "line": 44 - }, - "group": "clippy::nursery", - "docs": " **What it does:** This lint requires Future implementations returned from functions and methods to implement the `Send` marker trait. It is mostly\n used by library authors (public and internal) that target an audience where\n multithreaded executors are likely to be used for running these Futures.\n\n **Why is this bad?** A Future implementation captures some state that it\n needs to eventually produce its final value. When targeting a multithreaded\n executor (which is the norm on non-embedded devices) this means that this\n state may need to be transported to other threads, in other words the\n whole Future needs to implement the `Send` marker trait. If it does not,\n then the resulting Future cannot be submitted to a thread pool in the\n end user’s code.\n\n Especially for generic functions it can be confusing to leave the\n discovery of this problem to the end user: the reported error location\n will be far from its cause and can in many cases not even be fixed without\n modifying the library where the offending Future implementation is\n produced.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n async fn not_send(bytes: std::rc::Rc<[u8]>) {}\n ```\n Use instead:\n ```rust\n async fn is_send(bytes: std::sync::Arc<[u8]>) {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "get_last_with_len", - "id_span": { - "path": "src/get_last_with_len.rs", - "line": 40 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for using `x.get(x.len() - 1)` instead of `x.last()`.\n\n **Why is this bad?** Using `x.last()` is easier to read and has the same\n result.\n\n Note that using `x[x.len() - 1]` is semantically different from\n `x.last()`. Indexing into the array will panic on out-of-bounds\n accesses, while `x.get()` and `x.last()` will return `None`.\n\n There is another lint (get_unwrap) that covers the case of using\n `x.get(index).unwrap()` instead of `x[index]`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let x = vec![2, 3, 5];\n let last_element = x.get(x.len() - 1);\n\n // Good\n let x = vec![2, 3, 5];\n let last_element = x.last();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "identity_op", - "id_span": { - "path": "src/identity_op.rs", - "line": 24 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for identity operations, e.g., `x + 0`.\n **Why is this bad?** This code can be removed without changing the\n meaning. So it just obscures what's going on. Delete it mercilessly.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = 1;\n x / 1 + 0 * 1 - 0 | 0;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "if_let_mutex", - "id_span": { - "path": "src/if_let_mutex.rs", - "line": 36 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `Mutex::lock` calls in `if let` expression with lock calls in any of the else blocks.\n\n **Why is this bad?** The Mutex lock remains held for the whole\n `if let ... else` block and deadlocks.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,ignore\n if let Ok(thing) = mutex.lock() {\n do_thing();\n } else {\n mutex.lock();\n }\n ```\n Should be written\n ```rust,ignore\n let locked = mutex.lock();\n if let Ok(thing) = locked {\n do_thing(thing);\n } else {\n use_locked(locked);\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "if_let_some_result", - "id_span": { - "path": "src/if_let_some_result.rs", - "line": 34 - }, - "group": "clippy::style", - "docs": " **What it does:*** Checks for unnecessary `ok()` in if let.\n **Why is this bad?** Calling `ok()` in if let is unnecessary, instead match\n on `Ok(pat)`\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n for i in iter {\n if let Some(value) = i.parse().ok() {\n vec.push(value)\n }\n }\n ```\n Could be written:\n\n ```ignore\n for i in iter {\n if let Ok(value) = i.parse() {\n vec.push(value)\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "if_not_else", - "id_span": { - "path": "src/if_not_else.rs", - "line": 43 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for usage of `!` or `!=` in an if condition with an else branch.\n\n **Why is this bad?** Negations reduce the readability of statements.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let v: Vec = vec![];\n # fn a() {}\n # fn b() {}\n if !v.is_empty() {\n a()\n } else {\n b()\n }\n ```\n\n Could be written:\n\n ```rust\n # let v: Vec = vec![];\n # fn a() {}\n # fn b() {}\n if v.is_empty() {\n b()\n } else {\n a()\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "implicit_return", - "id_span": { - "path": "src/implicit_return.rs", - "line": 33 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for missing return statements at the end of a block.\n **Why is this bad?** Actually omitting the return keyword is idiomatic Rust code. Programmers\n coming from other languages might prefer the expressiveness of `return`. It's possible to miss\n the last returning statement because the only difference is a missing `;`. Especially in bigger\n code with multiple return paths having a `return` keyword makes it easier to find the\n corresponding statements.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn foo(x: usize) -> usize {\n x\n }\n ```\n add return\n ```rust\n fn foo(x: usize) -> usize {\n return x;\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "implicit_saturating_sub", - "id_span": { - "path": "src/implicit_saturating_sub.rs", - "line": 32 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for implicit saturating subtraction.\n **Why is this bad?** Simplicity and readability. Instead we can easily use an builtin function.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let end: u32 = 10;\n let start: u32 = 5;\n\n let mut i: u32 = end - start;\n\n // Bad\n if i != 0 {\n i -= 1;\n }\n\n // Good\n i = i.saturating_sub(1);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "inconsistent_struct_constructor", - "id_span": { - "path": "src/inconsistent_struct_constructor.rs", - "line": 58 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for struct constructors where the order of the field init shorthand in the constructor is inconsistent with the order in the struct definition.\n\n **Why is this bad?** Since the order of fields in a constructor doesn't affect the\n resulted instance as the below example indicates,\n\n ```rust\n #[derive(Debug, PartialEq, Eq)]\n struct Foo {\n x: i32,\n y: i32,\n }\n let x = 1;\n let y = 2;\n\n // This assertion never fails.\n assert_eq!(Foo { x, y }, Foo { y, x });\n ```\n\n inconsistent order means nothing and just decreases readability and consistency.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n struct Foo {\n x: i32,\n y: i32,\n }\n let x = 1;\n let y = 2;\n Foo { y, x };\n ```\n\n Use instead:\n ```rust\n # struct Foo {\n # x: i32,\n # y: i32,\n # }\n # let x = 1;\n # let y = 2;\n Foo { x, y };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "out_of_bounds_indexing", - "id_span": { - "path": "src/indexing_slicing.rs", - "line": 32 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for out of bounds array indexing with a constant index.\n\n **Why is this bad?** This will always panic at runtime.\n\n **Known problems:** Hopefully none.\n\n **Example:**\n ```no_run\n # #![allow(const_err)]\n let x = [1, 2, 3, 4];\n\n // Bad\n x[9];\n &x[2..9];\n\n // Good\n x[0];\n x[3];\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "indexing_slicing", - "id_span": { - "path": "src/indexing_slicing.rs", - "line": 81 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of indexing or slicing. Arrays are special cases, this lint does report on arrays if we can tell that slicing operations are in bounds and does not\n lint on constant `usize` indexing on arrays because that is handled by rustc's `const_err` lint.\n\n **Why is this bad?** Indexing and slicing can panic at runtime and there are\n safe alternatives.\n\n **Known problems:** Hopefully none.\n\n **Example:**\n ```rust,no_run\n // Vector\n let x = vec![0; 5];\n\n // Bad\n x[2];\n &x[2..100];\n &x[2..];\n &x[..100];\n\n // Good\n x.get(2);\n x.get(2..100);\n x.get(2..);\n x.get(..100);\n\n // Array\n let y = [0, 1, 2, 3];\n\n // Bad\n &y[10..100];\n &y[10..];\n &y[..100];\n\n // Good\n &y[2..];\n &y[..2];\n &y[0..3];\n y.get(10);\n y.get(10..100);\n y.get(10..);\n y.get(..100);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "infinite_iter", - "id_span": { - "path": "src/infinite_iter.rs", - "line": 21 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for iteration that is guaranteed to be infinite.\n **Why is this bad?** While there may be places where this is acceptable\n (e.g., in event streams), in most cases this is simply an error.\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n use std::iter;\n\n iter::repeat(1_u8).collect::>();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "maybe_infinite_iter", - "id_span": { - "path": "src/infinite_iter.rs", - "line": 40 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for iteration that may be infinite.\n **Why is this bad?** While there may be places where this is acceptable\n (e.g., in event streams), in most cases this is simply an error.\n\n **Known problems:** The code may have a condition to stop iteration, but\n this lint is not clever enough to analyze it.\n\n **Example:**\n ```rust\n let infinite_iter = 0..;\n [0..].iter().zip(infinite_iter.take_while(|x| *x > 5));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "multiple_inherent_impl", - "id_span": { - "path": "src/inherent_impl.rs", - "line": 37 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for multiple inherent implementations of a struct\n **Why is this bad?** Splitting the implementation of a type makes the code harder to navigate.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n struct X;\n impl X {\n fn one() {}\n }\n impl X {\n fn other() {}\n }\n ```\n\n Could be written:\n\n ```rust\n struct X;\n impl X {\n fn one() {}\n fn other() {}\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "inherent_to_string", - "id_span": { - "path": "src/inherent_to_string.rs", - "line": 44 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for the definition of inherent methods with a signature of `to_string(&self) -> String`.\n **Why is this bad?** This method is also implicitly defined if a type implements the `Display` trait. As the functionality of `Display` is much more versatile, it should be preferred.\n\n **Known problems:** None\n\n ** Example:**\n\n ```rust\n // Bad\n pub struct A;\n\n impl A {\n pub fn to_string(&self) -> String {\n \"I am A\".to_string()\n }\n }\n ```\n\n ```rust\n // Good\n use std::fmt;\n\n pub struct A;\n\n impl fmt::Display for A {\n fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n write!(f, \"I am A\")\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "inherent_to_string_shadow_display", - "id_span": { - "path": "src/inherent_to_string.rs", - "line": 89 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for the definition of inherent methods with a signature of `to_string(&self) -> String` and if the type implementing this method also implements the `Display` trait.\n **Why is this bad?** This method is also implicitly defined if a type implements the `Display` trait. The less versatile inherent method will then shadow the implementation introduced by `Display`.\n\n **Known problems:** None\n\n ** Example:**\n\n ```rust\n // Bad\n use std::fmt;\n\n pub struct A;\n\n impl A {\n pub fn to_string(&self) -> String {\n \"I am A\".to_string()\n }\n }\n\n impl fmt::Display for A {\n fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n write!(f, \"I am A, too\")\n }\n }\n ```\n\n ```rust\n // Good\n use std::fmt;\n\n pub struct A;\n\n impl fmt::Display for A {\n fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n write!(f, \"I am A\")\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "inline_fn_without_body", - "id_span": { - "path": "src/inline_fn_without_body.rs", - "line": 27 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `#[inline]` on trait methods without bodies\n **Why is this bad?** Only implementations of trait methods may be inlined.\n The inline attribute is ignored for trait methods without bodies.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n trait Animal {\n #[inline]\n fn name(&self) -> &'static str;\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "int_plus_one", - "id_span": { - "path": "src/int_plus_one.rs", - "line": 31 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `x >= y + 1` or `x - 1 >= y` (and `<=`) in a block\n **Why is this bad?** Readability -- better to use `> y` instead of `>= y + 1`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = 1;\n # let y = 1;\n if x >= y + 1 {}\n ```\n\n Could be written as:\n\n ```rust\n # let x = 1;\n # let y = 1;\n if x > y {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "integer_division", - "id_span": { - "path": "src/integer_division.rs", - "line": 26 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for division of integers\n **Why is this bad?** When outside of some very specific algorithms,\n integer division is very often a mistake because it discards the\n remainder.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n let x = 3 / 2;\n println!(\"{}\", x);\n\n // Good\n let x = 3f32 / 2f32;\n println!(\"{}\", x);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "items_after_statements", - "id_span": { - "path": "src/items_after_statements.rs", - "line": 48 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for items declared after some statement in a block.\n **Why is this bad?** Items live for the entire scope they are declared\n in. But statements are processed in order. This might cause confusion as\n it's hard to figure out which item is meant in a statement.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n fn foo() {\n println!(\"cake\");\n }\n\n fn main() {\n foo(); // prints \"foo\"\n fn foo() {\n println!(\"foo\");\n }\n foo(); // prints \"foo\"\n }\n ```\n\n ```rust\n // Good\n fn foo() {\n println!(\"cake\");\n }\n\n fn main() {\n fn foo() {\n println!(\"foo\");\n }\n foo(); // prints \"foo\"\n foo(); // prints \"foo\"\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "large_const_arrays", - "id_span": { - "path": "src/large_const_arrays.rs", - "line": 30 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for large `const` arrays that should be defined as `static` instead.\n\n **Why is this bad?** Performance: const variables are inlined upon use.\n Static items result in only one instance and has a fixed location in memory.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n // Bad\n pub const a = [0u32; 1_000_000];\n\n // Good\n pub static a = [0u32; 1_000_000];\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "large_enum_variant", - "id_span": { - "path": "src/large_enum_variant.rs", - "line": 39 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for large size differences between variants on `enum`s.\n\n **Why is this bad?** Enum size is bounded by the largest variant. Having a\n large variant can penalize the memory layout of that enum.\n\n **Known problems:** This lint obviously cannot take the distribution of\n variants in your running program into account. It is possible that the\n smaller variants make up less than 1% of all instances, in which case\n the overhead is negligible and the boxing is counter-productive. Always\n measure the change this lint suggests.\n\n **Example:**\n\n ```rust\n // Bad\n enum Test {\n A(i32),\n B([i32; 8000]),\n }\n\n // Possibly better\n enum Test2 {\n A(i32),\n B(Box<[i32; 8000]>),\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "large_stack_arrays", - "id_span": { - "path": "src/large_stack_arrays.rs", - "line": 23 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for local arrays that may be too large.\n **Why is this bad?** Large local arrays may cause stack overflow.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n let a = [0u32; 1_000_000];\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "len_zero", - "id_span": { - "path": "src/len_zero.rs", - "line": 41 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for getting the length of something via `.len()` just to compare to zero, and suggests using `.is_empty()` where applicable.\n\n **Why is this bad?** Some structures can answer `.is_empty()` much faster\n than calculating their length. So it is good to get into the habit of using\n `.is_empty()`, and having it is cheap.\n Besides, it makes the intent clearer than a manual comparison in some contexts.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n if x.len() == 0 {\n ..\n }\n if y.len() != 0 {\n ..\n }\n ```\n instead use\n ```ignore\n if x.is_empty() {\n ..\n }\n if !y.is_empty() {\n ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "len_without_is_empty", - "id_span": { - "path": "src/len_zero.rs", - "line": 66 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for items that implement `.len()` but not `.is_empty()`.\n\n **Why is this bad?** It is good custom to have both methods, because for\n some data structures, asking about the length will be a costly operation,\n whereas `.is_empty()` can usually answer in constant time. Also it used to\n lead to false positives on the [`len_zero`](#len_zero) lint – currently that\n lint will ignore such entities.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n impl X {\n pub fn len(&self) -> usize {\n ..\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "comparison_to_empty", - "id_span": { - "path": "src/len_zero.rs", - "line": 103 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for comparing to an empty slice such as `\"\"` or `[]`, and suggests using `.is_empty()` where applicable.\n\n **Why is this bad?** Some structures can answer `.is_empty()` much faster\n than checking for equality. So it is good to get into the habit of using\n `.is_empty()`, and having it is cheap.\n Besides, it makes the intent clearer than a manual comparison in some contexts.\n\n **Known problems:** None.\n\n **Example:**\n\n ```ignore\n if s == \"\" {\n ..\n }\n\n if arr == [] {\n ..\n }\n ```\n Use instead:\n ```ignore\n if s.is_empty() {\n ..\n }\n\n if arr.is_empty() {\n ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "useless_let_if_seq", - "id_span": { - "path": "src/let_if_seq.rs", - "line": 49 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for variable declarations immediately followed by a conditional affectation.\n\n **Why is this bad?** This is not idiomatic Rust.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n let foo;\n\n if bar() {\n foo = 42;\n } else {\n foo = 0;\n }\n\n let mut baz = None;\n\n if bar() {\n baz = Some(42);\n }\n ```\n\n should be written\n\n ```rust,ignore\n let foo = if bar() {\n 42\n } else {\n 0\n };\n\n let baz = if bar() {\n Some(42)\n } else {\n None\n };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "HasPlaceholders" - } - }, - { - "id": "let_underscore_must_use", - "id_span": { - "path": "src/let_underscore.rs", - "line": 29 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for `let _ = ` where expr is #[must_use]\n\n **Why is this bad?** It's better to explicitly\n handle the value of a #[must_use] expr\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn f() -> Result {\n Ok(0)\n }\n\n let _ = f();\n // is_ok() is marked #[must_use]\n let _ = f().is_ok();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "let_underscore_lock", - "id_span": { - "path": "src/let_underscore.rs", - "line": 56 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `let _ = sync_lock`\n **Why is this bad?** This statement immediately drops the lock instead of\n extending its lifetime to the end of the scope, which is often not intended.\n To extend lock lifetime to the end of the scope, use an underscore-prefixed\n name instead (i.e. _lock). If you want to explicitly drop the lock,\n `std::mem::drop` conveys your intention better and is less error-prone.\n\n **Known problems:** None.\n\n **Example:**\n\n Bad:\n ```rust,ignore\n let _ = mutex.lock();\n ```\n\n Good:\n ```rust,ignore\n let _lock = mutex.lock();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "let_underscore_drop", - "id_span": { - "path": "src/let_underscore.rs", - "line": 97 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for `let _ = ` where expr has a type that implements `Drop`\n\n **Why is this bad?** This statement immediately drops the initializer\n expression instead of extending its lifetime to the end of the scope, which\n is often not intended. To extend the expression's lifetime to the end of the\n scope, use an underscore-prefixed name instead (i.e. _var). If you want to\n explicitly drop the expression, `std::mem::drop` conveys your intention\n better and is less error-prone.\n\n **Known problems:** None.\n\n **Example:**\n\n Bad:\n ```rust,ignore\n struct Droppable;\n impl Drop for Droppable {\n fn drop(&mut self) {}\n }\n {\n let _ = Droppable;\n // ^ dropped here\n /* more code */\n }\n ```\n\n Good:\n ```rust,ignore\n {\n let _droppable = Droppable;\n /* more code */\n // dropped at end of scope\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "needless_lifetimes", - "id_span": { - "path": "src/lifetimes.rs", - "line": 46 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for lifetime annotations which can be removed by relying on lifetime elision.\n\n **Why is this bad?** The additional lifetimes make the code look more\n complicated, while there is nothing out of the ordinary going on. Removing\n them leads to more readable code.\n\n **Known problems:**\n - We bail out if the function has a `where` clause where lifetimes\n are mentioned due to potenial false positives.\n - Lifetime bounds such as `impl Foo + 'a` and `T: 'a` must be elided with the\n placeholder notation `'_` because the fully elided notation leaves the type bound to `'static`.\n\n **Example:**\n ```rust\n // Bad: unnecessary lifetime annotations\n fn in_and_out<'a>(x: &'a u8, y: u8) -> &'a u8 {\n x\n }\n\n // Good\n fn elided(x: &u8, y: u8) -> &u8 {\n x\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "extra_unused_lifetimes", - "id_span": { - "path": "src/lifetimes.rs", - "line": 74 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for lifetimes in generics that are never used anywhere else.\n\n **Why is this bad?** The additional lifetimes make the code look more\n complicated, while there is nothing out of the ordinary going on. Removing\n them leads to more readable code.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad: unnecessary lifetimes\n fn unused_lifetime<'a>(x: u8) {\n // ..\n }\n\n // Good\n fn no_lifetime(x: u8) {\n // ...\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unreadable_literal", - "id_span": { - "path": "src/literal_representation.rs", - "line": 33 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Warns if a long integral or floating-point constant does not contain underscores.\n\n **Why is this bad?** Reading long numbers is difficult without separators.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let x: u64 = 61864918973511;\n\n // Good\n let x: u64 = 61_864_918_973_511;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "mistyped_literal_suffixes", - "id_span": { - "path": "src/literal_representation.rs", - "line": 57 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Warns for mistyped suffix in literals\n **Why is this bad?** This is most probably a typo\n\n **Known problems:**\n - Recommends a signed suffix, even though the number might be too big and an unsigned\n suffix is required\n - Does not match on `_127` since that is a valid grouping for decimal and octal numbers\n\n **Example:**\n\n ```rust\n // Probably mistyped\n 2_32;\n\n // Good\n 2_i32;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "inconsistent_digit_grouping", - "id_span": { - "path": "src/literal_representation.rs", - "line": 80 - }, - "group": "clippy::style", - "docs": " **What it does:** Warns if an integral or floating-point constant is grouped inconsistently with underscores.\n\n **Why is this bad?** Readers may incorrectly interpret inconsistently\n grouped digits.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let x: u64 = 618_64_9189_73_511;\n\n // Good\n let x: u64 = 61_864_918_973_511;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unusual_byte_groupings", - "id_span": { - "path": "src/literal_representation.rs", - "line": 99 - }, - "group": "clippy::style", - "docs": " **What it does:** Warns if hexadecimal or binary literals are not grouped by nibble or byte.\n\n **Why is this bad?** Negatively impacts readability.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let x: u32 = 0xFFF_FFF;\n let y: u8 = 0b01_011_101;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "large_digit_groups", - "id_span": { - "path": "src/literal_representation.rs", - "line": 118 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Warns if the digits of an integral or floating-point constant are grouped into groups that\n are too large.\n\n **Why is this bad?** Negatively impacts readability.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let x: u64 = 6186491_8973511;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "decimal_literal_representation", - "id_span": { - "path": "src/literal_representation.rs", - "line": 136 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Warns if there is a better representation for a numeric literal.\n **Why is this bad?** Especially for big powers of 2 a hexadecimal representation is more\n readable than a decimal representation.\n\n **Known problems:** None.\n\n **Example:**\n\n `255` => `0xFF`\n `65_535` => `0xFFFF`\n `4_042_322_160` => `0xF0F0_F0F0`\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "manual_memcpy", - "id_span": { - "path": "src/loops/mod.rs", - "line": 50 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for for-loops that manually copy items between slices that could be optimized by having a memcpy.\n\n **Why is this bad?** It is not as fast as a memcpy.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let src = vec![1];\n # let mut dst = vec![0; 65];\n for i in 0..src.len() {\n dst[i + 64] = src[i];\n }\n ```\n Could be written as:\n ```rust\n # let src = vec![1];\n # let mut dst = vec![0; 65];\n dst[64..(src.len() + 64)].clone_from_slice(&src[..]);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "needless_range_loop", - "id_span": { - "path": "src/loops/mod.rs", - "line": 78 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for looping over the range of `0..len` of some collection just to get the values by index.\n\n **Why is this bad?** Just iterating the collection itself makes the intent\n more clear and is probably faster.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let vec = vec!['a', 'b', 'c'];\n for i in 0..vec.len() {\n println!(\"{}\", vec[i]);\n }\n ```\n Could be written as:\n ```rust\n let vec = vec!['a', 'b', 'c'];\n for i in vec {\n println!(\"{}\", i);\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "explicit_iter_loop", - "id_span": { - "path": "src/loops/mod.rs", - "line": 107 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for loops on `x.iter()` where `&x` will do, and suggests the latter.\n\n **Why is this bad?** Readability.\n\n **Known problems:** False negatives. We currently only warn on some known\n types.\n\n **Example:**\n ```rust\n // with `y` a `Vec` or slice:\n # let y = vec![1];\n for x in y.iter() {\n // ..\n }\n ```\n can be rewritten to\n ```rust\n # let y = vec![1];\n for x in &y {\n // ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "explicit_into_iter_loop", - "id_span": { - "path": "src/loops/mod.rs", - "line": 135 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for loops on `y.into_iter()` where `y` will do, and suggests the latter.\n\n **Why is this bad?** Readability.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n # let y = vec![1];\n // with `y` a `Vec` or slice:\n for x in y.into_iter() {\n // ..\n }\n ```\n can be rewritten to\n ```rust\n # let y = vec![1];\n for x in y {\n // ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "iter_next_loop", - "id_span": { - "path": "src/loops/mod.rs", - "line": 158 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for loops on `x.next()`.\n **Why is this bad?** `next()` returns either `Some(value)` if there was a\n value, or `None` otherwise. The insidious thing is that `Option<_>`\n implements `IntoIterator`, so that possibly one value will be iterated,\n leading to some hard to find bugs. No one will want to write such code\n [except to win an Underhanded Rust\n Contest](https://www.reddit.com/r/rust/comments/3hb0wm/underhanded_rust_contest/cu5yuhr).\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n for x in y.next() {\n ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "for_loops_over_fallibles", - "id_span": { - "path": "src/loops/mod.rs", - "line": 201 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `for` loops over `Option` or `Result` values.\n **Why is this bad?** Readability. This is more clearly expressed as an `if\n let`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let opt = Some(1);\n\n // Bad\n for x in opt {\n // ..\n }\n\n // Good\n if let Some(x) = opt {\n // ..\n }\n ```\n\n // or\n\n ```rust\n # let res: Result = Ok(1);\n\n // Bad\n for x in &res {\n // ..\n }\n\n // Good\n if let Ok(x) = res {\n // ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "while_let_loop", - "id_span": { - "path": "src/loops/mod.rs", - "line": 230 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Detects `loop + match` combinations that are easier written as a `while let` loop.\n\n **Why is this bad?** The `while let` loop is usually shorter and more\n readable.\n\n **Known problems:** Sometimes the wrong binding is displayed ([#383](https://github.com/rust-lang/rust-clippy/issues/383)).\n\n **Example:**\n ```rust,no_run\n # let y = Some(1);\n loop {\n let x = match y {\n Some(x) => x,\n None => break,\n };\n // .. do something with x\n }\n // is easier written as\n while let Some(x) = y {\n // .. do something with x\n };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "HasPlaceholders" - } - }, - { - "id": "needless_collect", - "id_span": { - "path": "src/loops/mod.rs", - "line": 252 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for functions collecting an iterator when collect is not needed.\n\n **Why is this bad?** `collect` causes the allocation of a new data structure,\n when this allocation may not be needed.\n\n **Known problems:**\n None\n\n **Example:**\n ```rust\n # let iterator = vec![1].into_iter();\n let len = iterator.clone().collect::>().len();\n // should be\n let len = iterator.count();\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MachineApplicable" - } - }, - { - "id": "explicit_counter_loop", - "id_span": { - "path": "src/loops/mod.rs", - "line": 282 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks `for` loops over slices with an explicit counter and suggests the use of `.enumerate()`.\n\n **Why is it bad?** Using `.enumerate()` makes the intent more clear,\n declutters the code and may be faster in some instances.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let v = vec![1];\n # fn bar(bar: usize, baz: usize) {}\n let mut i = 0;\n for item in &v {\n bar(i, *item);\n i += 1;\n }\n ```\n Could be written as\n ```rust\n # let v = vec![1];\n # fn bar(bar: usize, baz: usize) {}\n for (i, item) in v.iter().enumerate() { bar(i, *item); }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "empty_loop", - "id_span": { - "path": "src/loops/mod.rs", - "line": 315 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for empty `loop` expressions.\n **Why is this bad?** These busy loops burn CPU cycles without doing\n anything. It is _almost always_ a better idea to `panic!` than to have\n a busy loop.\n\n If panicking isn't possible, think of the environment and either:\n - block on something\n - sleep the thread for some microseconds\n - yield or pause the thread\n\n For `std` targets, this can be done with\n [`std::thread::sleep`](https://doc.rust-lang.org/std/thread/fn.sleep.html)\n or [`std::thread::yield_now`](https://doc.rust-lang.org/std/thread/fn.yield_now.html).\n\n For `no_std` targets, doing this is more complicated, especially because\n `#[panic_handler]`s can't panic. To stop/pause the thread, you will\n probably need to invoke some target-specific intrinsic. Examples include:\n - [`x86_64::instructions::hlt`](https://docs.rs/x86_64/0.12.2/x86_64/instructions/fn.hlt.html)\n - [`cortex_m::asm::wfi`](https://docs.rs/cortex-m/0.6.3/cortex_m/asm/fn.wfi.html)\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n loop {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "while_let_on_iterator", - "id_span": { - "path": "src/loops/mod.rs", - "line": 334 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `while let` expressions on iterators.\n **Why is this bad?** Readability. A simple `for` loop is shorter and conveys\n the intent better.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n while let Some(val) = iter() {\n ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "for_kv_map", - "id_span": { - "path": "src/loops/mod.rs", - "line": 362 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for iterating a map (`HashMap` or `BTreeMap`) and ignoring either the keys or values.\n\n **Why is this bad?** Readability. There are `keys` and `values` methods that\n can be used to express that don't need the values or keys.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n for (k, _) in &map {\n ..\n }\n ```\n\n could be replaced by\n\n ```ignore\n for k in map.keys() {\n ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "never_loop", - "id_span": { - "path": "src/loops/mod.rs", - "line": 383 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for loops that will always `break`, `return` or `continue` an outer loop.\n\n **Why is this bad?** This loop never loops, all it does is obfuscating the\n code.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n loop {\n ..;\n break;\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mut_range_bound", - "id_span": { - "path": "src/loops/mod.rs", - "line": 403 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for loops which have a range bound that is a mutable variable\n **Why is this bad?** One might think that modifying the mutable variable changes the loop bounds\n\n **Known problems:** None\n\n **Example:**\n ```rust\n let mut foo = 42;\n for i in 0..foo {\n foo -= 1;\n println!(\"{}\", i); // prints numbers from 0 to 42, not 0 to 21\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "while_immutable_condition", - "id_span": { - "path": "src/loops/mod.rs", - "line": 426 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks whether variables used within while loop condition can be (and are) mutated in the body.\n\n **Why is this bad?** If the condition is unchanged, entering the body of the loop\n will lead to an infinite loop.\n\n **Known problems:** If the `while`-loop is in a closure, the check for mutation of the\n condition variables in the body can cause false negatives. For example when only `Upvar` `a` is\n in the condition and only `Upvar` `b` gets mutated in the body, the lint will not trigger.\n\n **Example:**\n ```rust\n let i = 0;\n while i > 10 {\n println!(\"let me loop forever!\");\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "same_item_push", - "id_span": { - "path": "src/loops/mod.rs", - "line": 459 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks whether a for loop is being used to push a constant value into a Vec.\n\n **Why is this bad?** This kind of operation can be expressed more succinctly with\n `vec![item;SIZE]` or `vec.resize(NEW_SIZE, item)` and using these alternatives may also\n have better performance.\n **Known problems:** None\n\n **Example:**\n ```rust\n let item1 = 2;\n let item2 = 3;\n let mut vec: Vec = Vec::new();\n for _ in 0..20 {\n vec.push(item1);\n }\n for _ in 0..30 {\n vec.push(item2);\n }\n ```\n could be written as\n ```rust\n let item1 = 2;\n let item2 = 3;\n let mut vec: Vec = vec![item1; 20];\n vec.resize(20 + 30, item2);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "single_element_loop", - "id_span": { - "path": "src/loops/mod.rs", - "line": 484 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks whether a for loop has a single element.\n **Why is this bad?** There is no reason to have a loop of a\n single element.\n **Known problems:** None\n\n **Example:**\n ```rust\n let item1 = 2;\n for item in &[item1] {\n println!(\"{}\", item);\n }\n ```\n could be written as\n ```rust\n let item1 = 2;\n let item = &item1;\n println!(\"{}\", item);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "manual_flatten", - "id_span": { - "path": "src/loops/mod.rs", - "line": 515 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Check for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the iterator element is used.\n\n **Why is this bad?** It is verbose and can be simplified\n by first calling the `flatten` method on the `Iterator`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let x = vec![Some(1), Some(2), Some(3)];\n for n in x {\n if let Some(n) = n {\n println!(\"{}\", n);\n }\n }\n ```\n Use instead:\n ```rust\n let x = vec![Some(1), Some(2), Some(3)];\n for n in x.into_iter().flatten() {\n println!(\"{}\", n);\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "macro_use_imports", - "id_span": { - "path": "src/macro_use.rs", - "line": 25 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for `#[macro_use] use...`.\n **Why is this bad?** Since the Rust 2018 edition you can import\n macro's directly, this is considered idiomatic.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n #[macro_use]\n use some_macro;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "main_recursion", - "id_span": { - "path": "src/main_recursion.rs", - "line": 22 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for recursion using the entrypoint.\n **Why is this bad?** Apart from special setups (which we could detect following attributes like #![no_std]),\n recursing into main() seems like an unintuitive antipattern we should be able to detect.\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n fn main() {\n main();\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "manual_async_fn", - "id_span": { - "path": "src/manual_async_fn.rs", - "line": 32 - }, - "group": "clippy::style", - "docs": " **What it does:** It checks for manual implementations of `async` functions.\n **Why is this bad?** It's more idiomatic to use the dedicated syntax.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n use std::future::Future;\n\n fn foo() -> impl Future { async { 42 } }\n ```\n Use instead:\n ```rust\n async fn foo() -> i32 { 42 }\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MachineApplicable" - } - }, - { - "id": "manual_map", - "id_span": { - "path": "src/manual_map.rs", - "line": 44 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usages of `match` which could be implemented using `map`\n **Why is this bad?** Using the `map` method is clearer and more concise.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n match Some(0) {\n Some(x) => Some(x + 1),\n None => None,\n };\n ```\n Use instead:\n ```rust\n Some(0).map(|x| x + 1);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "manual_non_exhaustive", - "id_span": { - "path": "src/manual_non_exhaustive.rs", - "line": 56 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for manual implementations of the non-exhaustive pattern.\n **Why is this bad?** Using the #[non_exhaustive] attribute expresses better the intent\n and allows possible optimizations when applied to enums.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n struct S {\n pub a: i32,\n pub b: i32,\n _c: (),\n }\n\n enum E {\n A,\n B,\n #[doc(hidden)]\n _C,\n }\n\n struct T(pub i32, pub i32, ());\n ```\n Use instead:\n ```rust\n #[non_exhaustive]\n struct S {\n pub a: i32,\n pub b: i32,\n }\n\n #[non_exhaustive]\n enum E {\n A,\n B,\n }\n\n #[non_exhaustive]\n struct T(pub i32, pub i32);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "manual_ok_or", - "id_span": { - "path": "src/manual_ok_or.rs", - "line": 34 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Finds patterns that reimplement `Option::ok_or`.\n\n **Why is this bad?**\n Concise code helps focusing on behavior instead of boilerplate.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n let foo: Option = None;\n foo.map_or(Err(\"error\"), |v| Ok(v));\n ```\n\n Use instead:\n ```rust\n let foo: Option = None;\n foo.ok_or(\"error\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "manual_strip", - "id_span": { - "path": "src/manual_strip.rs", - "line": 52 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing using\n the pattern's length.\n\n **Why is this bad?**\n Using `str:strip_{prefix,suffix}` is safer and may have better performance as there is no\n slicing which may panic and the compiler does not need to insert this panic code. It is\n also sometimes more readable as it removes the need for duplicating or storing the pattern\n used by `str::{starts,ends}_with` and in the slicing.\n\n **Known problems:**\n None.\n\n **Example:**\n\n ```rust\n let s = \"hello, world!\";\n if s.starts_with(\"hello, \") {\n assert_eq!(s[\"hello, \".len()..].to_uppercase(), \"WORLD!\");\n }\n ```\n Use instead:\n ```rust\n let s = \"hello, world!\";\n if let Some(end) = s.strip_prefix(\"hello, \") {\n assert_eq!(end.to_uppercase(), \"WORLD!\");\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "manual_unwrap_or", - "id_span": { - "path": "src/manual_unwrap_or.rs", - "line": 36 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`.\n\n **Why is this bad?**\n Concise code helps focusing on behavior instead of boilerplate.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let foo: Option = None;\n match foo {\n Some(v) => v,\n None => 1,\n };\n ```\n\n Use instead:\n ```rust\n let foo: Option = None;\n foo.unwrap_or(1);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "map_clone", - "id_span": { - "path": "src/map_clone.rs", - "line": 40 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usage of `map(|x| x.clone())` or dereferencing closures for `Copy` types, on `Iterator` or `Option`,\n and suggests `cloned()` or `copied()` instead\n\n **Why is this bad?** Readability, this can be written more concisely\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n let x = vec![42, 43];\n let y = x.iter();\n let z = y.map(|i| *i);\n ```\n\n The correct use would be:\n\n ```rust\n let x = vec![42, 43];\n let y = x.iter();\n let z = y.cloned();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "map_err_ignore", - "id_span": { - "path": "src/map_err_ignore.rs", - "line": 101 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for instances of `map_err(|_| Some::Enum)`\n **Why is this bad?** This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error\n\n **Known problems:** None.\n\n **Example:**\n Before:\n ```rust\n use std::fmt;\n\n #[derive(Debug)]\n enum Error {\n Indivisible,\n Remainder(u8),\n }\n\n impl fmt::Display for Error {\n fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n match self {\n Error::Indivisible => write!(f, \"could not divide input by three\"),\n Error::Remainder(remainder) => write!(\n f,\n \"input is not divisible by three, remainder = {}\",\n remainder\n ),\n }\n }\n }\n\n impl std::error::Error for Error {}\n\n fn divisible_by_3(input: &str) -> Result<(), Error> {\n input\n .parse::()\n .map_err(|_| Error::Indivisible)\n .map(|v| v % 3)\n .and_then(|remainder| {\n if remainder == 0 {\n Ok(())\n } else {\n Err(Error::Remainder(remainder as u8))\n }\n })\n }\n ```\n\n After:\n ```rust\n use std::{fmt, num::ParseIntError};\n\n #[derive(Debug)]\n enum Error {\n Indivisible(ParseIntError),\n Remainder(u8),\n }\n\n impl fmt::Display for Error {\n fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n match self {\n Error::Indivisible(_) => write!(f, \"could not divide input by three\"),\n Error::Remainder(remainder) => write!(\n f,\n \"input is not divisible by three, remainder = {}\",\n remainder\n ),\n }\n }\n }\n\n impl std::error::Error for Error {\n fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {\n match self {\n Error::Indivisible(source) => Some(source),\n _ => None,\n }\n }\n }\n\n fn divisible_by_3(input: &str) -> Result<(), Error> {\n input\n .parse::()\n .map_err(Error::Indivisible)\n .map(|v| v % 3)\n .and_then(|remainder| {\n if remainder == 0 {\n Ok(())\n } else {\n Err(Error::Remainder(remainder as u8))\n }\n })\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "map_identity", - "id_span": { - "path": "src/map_identity.rs", - "line": 30 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for instances of `map(f)` where `f` is the identity function.\n **Why is this bad?** It can be written more concisely without the call to `map`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let x = [1, 2, 3];\n let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();\n ```\n Use instead:\n ```rust\n let x = [1, 2, 3];\n let y: Vec<_> = x.iter().map(|x| 2*x).collect();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "option_map_unit_fn", - "id_span": { - "path": "src/map_unit_fn.rs", - "line": 48 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `option.map(f)` where f is a function or closure that returns the unit type `()`.\n\n **Why is this bad?** Readability, this can be written more clearly with\n an if let statement\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # fn do_stuff() -> Option { Some(String::new()) }\n # fn log_err_msg(foo: String) -> Option { Some(foo) }\n # fn format_msg(foo: String) -> String { String::new() }\n let x: Option = do_stuff();\n x.map(log_err_msg);\n # let x: Option = do_stuff();\n x.map(|msg| log_err_msg(format_msg(msg)));\n ```\n\n The correct use would be:\n\n ```rust\n # fn do_stuff() -> Option { Some(String::new()) }\n # fn log_err_msg(foo: String) -> Option { Some(foo) }\n # fn format_msg(foo: String) -> String { String::new() }\n let x: Option = do_stuff();\n if let Some(msg) = x {\n log_err_msg(msg);\n }\n\n # let x: Option = do_stuff();\n if let Some(msg) = x {\n log_err_msg(format_msg(msg));\n }\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MachineApplicable" - } - }, - { - "id": "result_map_unit_fn", - "id_span": { - "path": "src/map_unit_fn.rs", - "line": 89 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `result.map(f)` where f is a function or closure that returns the unit type `()`.\n\n **Why is this bad?** Readability, this can be written more clearly with\n an if let statement\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # fn do_stuff() -> Result { Ok(String::new()) }\n # fn log_err_msg(foo: String) -> Result { Ok(foo) }\n # fn format_msg(foo: String) -> String { String::new() }\n let x: Result = do_stuff();\n x.map(log_err_msg);\n # let x: Result = do_stuff();\n x.map(|msg| log_err_msg(format_msg(msg)));\n ```\n\n The correct use would be:\n\n ```rust\n # fn do_stuff() -> Result { Ok(String::new()) }\n # fn log_err_msg(foo: String) -> Result { Ok(foo) }\n # fn format_msg(foo: String) -> String { String::new() }\n let x: Result = do_stuff();\n if let Ok(msg) = x {\n log_err_msg(msg);\n };\n # let x: Result = do_stuff();\n if let Ok(msg) = x {\n log_err_msg(format_msg(msg));\n };\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MachineApplicable" - } - }, - { - "id": "match_on_vec_items", - "id_span": { - "path": "src/match_on_vec_items.rs", - "line": 41 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for `match vec[idx]` or `match vec[n..m]`.\n **Why is this bad?** This can panic at runtime.\n\n **Known problems:** None.\n\n **Example:**\n ```rust, no_run\n let arr = vec![0, 1, 2, 3];\n let idx = 1;\n\n // Bad\n match arr[idx] {\n 0 => println!(\"{}\", 0),\n 1 => println!(\"{}\", 3),\n _ => {},\n }\n ```\n Use instead:\n ```rust, no_run\n let arr = vec![0, 1, 2, 3];\n let idx = 1;\n\n // Good\n match arr.get(idx) {\n Some(0) => println!(\"{}\", 0),\n Some(1) => println!(\"{}\", 3),\n _ => {},\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "single_match", - "id_span": { - "path": "src/matches.rs", - "line": 55 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for matches with a single arm where an `if let` will usually suffice.\n\n **Why is this bad?** Just readability – `if let` nests less than a `match`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn bar(stool: &str) {}\n # let x = Some(\"abc\");\n // Bad\n match x {\n Some(ref foo) => bar(foo),\n _ => (),\n }\n\n // Good\n if let Some(ref foo) = x {\n bar(foo);\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "HasPlaceholders" - } - }, - { - "id": "single_match_else", - "id_span": { - "path": "src/matches.rs", - "line": 94 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for matches with two arms where an `if let else` will usually suffice.\n\n **Why is this bad?** Just readability – `if let` nests less than a `match`.\n\n **Known problems:** Personal style preferences may differ.\n\n **Example:**\n\n Using `match`:\n\n ```rust\n # fn bar(foo: &usize) {}\n # let other_ref: usize = 1;\n # let x: Option<&usize> = Some(&1);\n match x {\n Some(ref foo) => bar(foo),\n _ => bar(&other_ref),\n }\n ```\n\n Using `if let` with `else`:\n\n ```rust\n # fn bar(foo: &usize) {}\n # let other_ref: usize = 1;\n # let x: Option<&usize> = Some(&1);\n if let Some(ref foo) = x {\n bar(foo);\n } else {\n bar(&other_ref);\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "HasPlaceholders" - } - }, - { - "id": "match_ref_pats", - "id_span": { - "path": "src/matches.rs", - "line": 125 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for matches where all arms match a reference, suggesting to remove the reference and deref the matched expression\n instead. It also checks for `if let &foo = bar` blocks.\n\n **Why is this bad?** It just makes the code less readable. That reference\n destructuring adds nothing to the code.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n // Bad\n match x {\n &A(ref y) => foo(y),\n &B => bar(),\n _ => frob(&x),\n }\n\n // Good\n match *x {\n A(ref y) => foo(y),\n B => bar(),\n _ => frob(x),\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "match_bool", - "id_span": { - "path": "src/matches.rs", - "line": 159 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for matches where match expression is a `bool`. It suggests to replace the expression with an `if...else` block.\n\n **Why is this bad?** It makes the code less readable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn foo() {}\n # fn bar() {}\n let condition: bool = true;\n match condition {\n true => foo(),\n false => bar(),\n }\n ```\n Use if/else instead:\n ```rust\n # fn foo() {}\n # fn bar() {}\n let condition: bool = true;\n if condition {\n foo();\n } else {\n bar();\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "HasPlaceholders" - } - }, - { - "id": "match_overlapping_arm", - "id_span": { - "path": "src/matches.rs", - "line": 181 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for overlapping match arms.\n **Why is this bad?** It is likely to be an error and if not, makes the code\n less obvious.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = 5;\n match x {\n 1...10 => println!(\"1 ... 10\"),\n 5...15 => println!(\"5 ... 15\"),\n _ => (),\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "match_wild_err_arm", - "id_span": { - "path": "src/matches.rs", - "line": 203 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for arm which matches all errors with `Err(_)` and take drastic actions like `panic!`.\n\n **Why is this bad?** It is generally a bad practice, similar to\n catching all exceptions in java with `catch(Exception)`\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x: Result = Ok(3);\n match x {\n Ok(_) => println!(\"ok\"),\n Err(_) => panic!(\"err\"),\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "match_as_ref", - "id_span": { - "path": "src/matches.rs", - "line": 229 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for match which is used to add a reference to an `Option` value.\n\n **Why is this bad?** Using `as_ref()` or `as_mut()` instead is shorter.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x: Option<()> = None;\n\n // Bad\n let r: Option<&()> = match x {\n None => None,\n Some(ref v) => Some(v),\n };\n\n // Good\n let r: Option<&()> = x.as_ref();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "wildcard_enum_match_arm", - "id_span": { - "path": "src/matches.rs", - "line": 258 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for wildcard enum matches using `_`.\n **Why is this bad?** New enum variants added by library updates can be missed.\n\n **Known problems:** Suggested replacements may be incorrect if guards exhaustively cover some\n variants, and also may not use correct path to enum if it's not present in the current scope.\n\n **Example:**\n ```rust\n # enum Foo { A(usize), B(usize) }\n # let x = Foo::B(1);\n // Bad\n match x {\n Foo::A(_) => {},\n _ => {},\n }\n\n // Good\n match x {\n Foo::A(_) => {},\n Foo::B(_) => {},\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "match_wildcard_for_single_variants", - "id_span": { - "path": "src/matches.rs", - "line": 290 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for wildcard enum matches for a single variant.\n **Why is this bad?** New enum variants added by library updates can be missed.\n\n **Known problems:** Suggested replacements may not use correct path to enum\n if it's not present in the current scope.\n\n **Example:**\n\n ```rust\n # enum Foo { A, B, C }\n # let x = Foo::B;\n // Bad\n match x {\n Foo::A => {},\n Foo::B => {},\n _ => {},\n }\n\n // Good\n match x {\n Foo::A => {},\n Foo::B => {},\n Foo::C => {},\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "wildcard_in_or_patterns", - "id_span": { - "path": "src/matches.rs", - "line": 317 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for wildcard pattern used with others patterns in same match arm.\n **Why is this bad?** Wildcard pattern already covers any other pattern as it will match anyway.\n It makes the code less readable, especially to spot wildcard pattern use in match arm.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n match \"foo\" {\n \"a\" => {},\n \"bar\" | _ => {},\n }\n\n // Good\n match \"foo\" {\n \"a\" => {},\n _ => {},\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "infallible_destructuring_match", - "id_span": { - "path": "src/matches.rs", - "line": 352 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for matches being used to destructure a single-variant enum or tuple struct where a `let` will suffice.\n\n **Why is this bad?** Just readability – `let` doesn't nest, whereas a `match` does.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n enum Wrapper {\n Data(i32),\n }\n\n let wrapper = Wrapper::Data(42);\n\n let data = match wrapper {\n Wrapper::Data(i) => i,\n };\n ```\n\n The correct use would be:\n ```rust\n enum Wrapper {\n Data(i32),\n }\n\n let wrapper = Wrapper::Data(42);\n let Wrapper::Data(data) = wrapper;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "match_single_binding", - "id_span": { - "path": "src/matches.rs", - "line": 380 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for useless match that binds to only one value.\n **Why is this bad?** Readability and needless complexity.\n\n **Known problems:** Suggested replacements may be incorrect when `match`\n is actually binding temporary value, bringing a 'dropped while borrowed' error.\n\n **Example:**\n ```rust\n # let a = 1;\n # let b = 2;\n\n // Bad\n match (a, b) {\n (c, d) => {\n // useless match\n }\n }\n\n // Good\n let (c, d) = (a, b);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "rest_pat_in_fully_bound_structs", - "id_span": { - "path": "src/matches.rs", - "line": 410 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched.\n **Why is this bad?** Correctness and readability. It's like having a wildcard pattern after\n matching all enum variants explicitly.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct A { a: i32 }\n let a = A { a: 5 };\n\n // Bad\n match a {\n A { a: 5, .. } => {},\n _ => {},\n }\n\n // Good\n match a {\n A { a: 5 } => {},\n _ => {},\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "redundant_pattern_matching", - "id_span": { - "path": "src/matches.rs", - "line": 458 - }, - "group": "clippy::style", - "docs": " **What it does:** Lint for redundant pattern matching over `Result`, `Option`, `std::task::Poll` or `std::net::IpAddr`\n\n **Why is this bad?** It's more concise and clear to just use the proper\n utility function\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # use std::task::Poll;\n # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};\n if let Ok(_) = Ok::(42) {}\n if let Err(_) = Err::(42) {}\n if let None = None::<()> {}\n if let Some(_) = Some(42) {}\n if let Poll::Pending = Poll::Pending::<()> {}\n if let Poll::Ready(_) = Poll::Ready(42) {}\n if let IpAddr::V4(_) = IpAddr::V4(Ipv4Addr::LOCALHOST) {}\n if let IpAddr::V6(_) = IpAddr::V6(Ipv6Addr::LOCALHOST) {}\n match Ok::(42) {\n Ok(_) => true,\n Err(_) => false,\n };\n ```\n\n The more idiomatic use would be:\n\n ```rust\n # use std::task::Poll;\n # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};\n if Ok::(42).is_ok() {}\n if Err::(42).is_err() {}\n if None::<()>.is_none() {}\n if Some(42).is_some() {}\n if Poll::Pending::<()>.is_pending() {}\n if Poll::Ready(42).is_ready() {}\n if IpAddr::V4(Ipv4Addr::LOCALHOST).is_ipv4() {}\n if IpAddr::V6(Ipv6Addr::LOCALHOST).is_ipv6() {}\n Ok::(42).is_ok();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "match_like_matches_macro", - "id_span": { - "path": "src/matches.rs", - "line": 491 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `match` or `if let` expressions producing a `bool` that could be written using `matches!`\n\n **Why is this bad?** Readability and needless complexity.\n\n **Known problems:** This lint falsely triggers, if there are arms with\n `cfg` attributes that remove an arm evaluating to `false`.\n\n **Example:**\n ```rust\n let x = Some(5);\n\n // Bad\n let a = match x {\n Some(0) => true,\n _ => false,\n };\n\n let a = if let Some(0) = x {\n true\n } else {\n false\n };\n\n // Good\n let a = matches!(x, Some(0));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "match_same_arms", - "id_span": { - "path": "src/matches.rs", - "line": 532 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for `match` with identical arm bodies.\n **Why is this bad?** This is probably a copy & paste error. If arm bodies\n are the same on purpose, you can factor them\n [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).\n\n **Known problems:** False positive possible with order dependent `match`\n (see issue\n [#860](https://github.com/rust-lang/rust-clippy/issues/860)).\n\n **Example:**\n ```rust,ignore\n match foo {\n Bar => bar(),\n Quz => quz(),\n Baz => bar(), // <= oops\n }\n ```\n\n This should probably be\n ```rust,ignore\n match foo {\n Bar => bar(),\n Quz => quz(),\n Baz => baz(), // <= fixed\n }\n ```\n\n or if the original code was not a typo:\n ```rust,ignore\n match foo {\n Bar | Baz => bar(), // <= shows the intent better\n Quz => quz(),\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mem_discriminant_non_enum", - "id_span": { - "path": "src/mem_discriminant.rs", - "line": 25 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for calls of `mem::discriminant()` on a non-enum type.\n **Why is this bad?** The value of `mem::discriminant()` on non-enum types\n is unspecified.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n use std::mem;\n\n mem::discriminant(&\"hello\");\n mem::discriminant(&&Some(2));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "mem_forget", - "id_span": { - "path": "src/mem_forget.rs", - "line": 21 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of `std::mem::forget(t)` where `t` is `Drop`.\n\n **Why is this bad?** `std::mem::forget(t)` prevents `t` from running its\n destructor, possibly causing leaks.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::mem;\n # use std::rc::Rc;\n mem::forget(Rc::new(55))\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mem_replace_option_with_none", - "id_span": { - "path": "src/mem_replace.rs", - "line": 37 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `mem::replace()` on an `Option` with `None`.\n\n **Why is this bad?** `Option` already has the method `take()` for\n taking its current value (Some(..) or None) and replacing it with\n `None`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n use std::mem;\n\n let mut an_option = Some(0);\n let replaced = mem::replace(&mut an_option, None);\n ```\n Is better expressed with:\n ```rust\n let mut an_option = Some(0);\n let taken = an_option.take();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "mem_replace_with_uninit", - "id_span": { - "path": "src/mem_replace.rs", - "line": 69 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `mem::replace(&mut _, mem::uninitialized())` and `mem::replace(&mut _, mem::zeroed())`.\n\n **Why is this bad?** This will lead to undefined behavior even if the\n value is overwritten later, because the uninitialized value may be\n observed in the case of a panic.\n\n **Known problems:** None.\n\n **Example:**\n\n ```\n use std::mem;\n# fn may_panic(v: Vec) -> Vec { v }\n\n #[allow(deprecated, invalid_value)]\n fn myfunc (v: &mut Vec) {\n let taken_v = unsafe { mem::replace(v, mem::uninitialized()) };\n let new_v = may_panic(taken_v); // undefined behavior on panic\n mem::forget(mem::replace(v, new_v));\n }\n ```\n\n The [take_mut](https://docs.rs/take_mut) crate offers a sound solution,\n at the cost of either lazily creating a replacement value or aborting\n on panic, to ensure that the uninitialized value cannot be observed.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mem_replace_with_default", - "id_span": { - "path": "src/mem_replace.rs", - "line": 93 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `std::mem::replace` on a value of type `T` with `T::default()`.\n\n **Why is this bad?** `std::mem` module already has the method `take` to\n take the current value and replace it with the default value of that type.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let mut text = String::from(\"foo\");\n let replaced = std::mem::replace(&mut text, String::default());\n ```\n Is better expressed with:\n ```rust\n let mut text = String::from(\"foo\");\n let taken = std::mem::take(&mut text);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unwrap_used", - "id_span": { - "path": "src/methods/mod.rs", - "line": 84 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s.\n **Why is this bad?** It is better to handle the `None` or `Err` case,\n or at least call `.expect(_)` with a more helpful message. Still, for a lot of\n quick-and-dirty code, `unwrap` is a good choice, which is why this lint is\n `Allow` by default.\n\n `result.unwrap()` will let the thread panic on `Err` values.\n Normally, you want to implement more sophisticated error handling,\n and propagate errors upwards with `?` operator.\n\n Even if you want to panic on errors, not all `Error`s implement good\n messages on display. Therefore, it may be beneficial to look at the places\n where they may get displayed. Activate this lint to do just that.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n # let opt = Some(1);\n\n // Bad\n opt.unwrap();\n\n // Good\n opt.expect(\"more helpful message\");\n ```\n\n // or\n\n ```rust\n # let res: Result = Ok(1);\n\n // Bad\n res.unwrap();\n\n // Good\n res.expect(\"more helpful message\");\n ```\n", - "applicability": null - }, - { - "id": "expect_used", - "id_span": { - "path": "src/methods/mod.rs", - "line": 126 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for `.expect()` calls on `Option`s and `Result`s.\n **Why is this bad?** Usually it is better to handle the `None` or `Err` case.\n Still, for a lot of quick-and-dirty code, `expect` is a good choice, which is why\n this lint is `Allow` by default.\n\n `result.expect()` will let the thread panic on `Err`\n values. Normally, you want to implement more sophisticated error handling,\n and propagate errors upwards with `?` operator.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust,ignore\n # let opt = Some(1);\n\n // Bad\n opt.expect(\"one\");\n\n // Good\n let opt = Some(1);\n opt?;\n ```\n\n // or\n\n ```rust\n # let res: Result = Ok(1);\n\n // Bad\n res.expect(\"one\");\n\n // Good\n res?;\n # Ok::<(), ()>(())\n ```\n", - "applicability": null - }, - { - "id": "should_implement_trait", - "id_span": { - "path": "src/methods/mod.rs", - "line": 155 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for methods that should live in a trait implementation of a `std` trait (see [llogiq's blog\n post](http://llogiq.github.io/2015/07/30/traits.html) for further\n information) instead of an inherent implementation.\n\n **Why is this bad?** Implementing the traits improve ergonomics for users of\n the code, often with very little cost. Also people seeing a `mul(...)`\n method\n may expect `*` to work equally, so you should have good reason to disappoint\n them.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n struct X;\n impl X {\n fn add(&self, other: &X) -> X {\n // ..\n # X\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "wrong_self_convention", - "id_span": { - "path": "src/methods/mod.rs", - "line": 188 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for methods with certain name prefixes and which doesn't match how self is taken. The actual rules are:\n\n |Prefix |`self` taken |\n |-------|----------------------|\n |`as_` |`&self` or `&mut self`|\n |`from_`| none |\n |`into_`|`self` |\n |`is_` |`&self` or none |\n |`to_` |`&self` |\n\n **Why is this bad?** Consistency breeds readability. If you follow the\n conventions, your users won't be surprised that they, e.g., need to supply a\n mutable reference to a `as_..` function.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct X;\n impl X {\n fn as_str(self) -> &'static str {\n // ..\n # \"\"\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "wrong_pub_self_convention", - "id_span": { - "path": "src/methods/mod.rs", - "line": 212 - }, - "group": "clippy::restriction", - "docs": " **What it does:** This is the same as [`wrong_self_convention`](#wrong_self_convention), but for public items.\n\n **Why is this bad?** See [`wrong_self_convention`](#wrong_self_convention).\n\n **Known problems:** Actually *renaming* the function may break clients if\n the function is part of the public interface. In that case, be mindful of\n the stability guarantees you've given your users.\n\n **Example:**\n ```rust\n # struct X;\n impl<'a> X {\n pub fn as_str(self) -> &'a str {\n \"foo\"\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "ok_expect", - "id_span": { - "path": "src/methods/mod.rs", - "line": 235 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usage of `ok().expect(..)`.\n **Why is this bad?** Because you usually call `expect()` on the `Result`\n directly to get a better error message.\n\n **Known problems:** The error type needs to implement `Debug`\n\n **Example:**\n ```rust\n # let x = Ok::<_, ()>(());\n\n // Bad\n x.ok().expect(\"why did I do this again?\");\n\n // Good\n x.expect(\"why did I do this again?\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "map_unwrap_or", - "id_span": { - "path": "src/methods/mod.rs", - "line": 272 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for usage of `option.map(_).unwrap_or(_)` or `option.map(_).unwrap_or_else(_)` or `result.map(_).unwrap_or_else(_)`.\n\n **Why is this bad?** Readability, these can be written more concisely (resp.) as\n `option.map_or(_, _)`, `option.map_or_else(_, _)` and `result.map_or_else(_, _)`.\n\n **Known problems:** The order of the arguments is not in execution order\n\n **Examples:**\n ```rust\n # let x = Some(1);\n\n // Bad\n x.map(|a| a + 1).unwrap_or(0);\n\n // Good\n x.map_or(0, |a| a + 1);\n ```\n\n // or\n\n ```rust\n # let x: Result = Ok(1);\n # fn some_function(foo: ()) -> usize { 1 }\n\n // Bad\n x.map(|a| a + 1).unwrap_or_else(some_function);\n\n // Good\n x.map_or_else(some_function, |a| a + 1);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "option_map_or_none", - "id_span": { - "path": "src/methods/mod.rs", - "line": 295 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usage of `_.map_or(None, _)`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.and_then(_)`.\n\n **Known problems:** The order of the arguments is not in execution order.\n\n **Example:**\n ```rust\n # let opt = Some(1);\n\n // Bad\n opt.map_or(None, |a| Some(a + 1));\n\n // Good\n opt.and_then(|a| Some(a + 1));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "result_map_or_into_option", - "id_span": { - "path": "src/methods/mod.rs", - "line": 321 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usage of `_.map_or(None, Some)`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.ok()`.\n\n **Known problems:** None.\n\n **Example:**\n\n Bad:\n ```rust\n # let r: Result = Ok(1);\n assert_eq!(Some(1), r.map_or(None, Some));\n ```\n\n Good:\n ```rust\n # let r: Result = Ok(1);\n assert_eq!(Some(1), r.ok());\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "bind_instead_of_map", - "id_span": { - "path": "src/methods/mod.rs", - "line": 354 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or `_.or_else(|x| Err(y))`.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.map(|x| y)` or `_.map_err(|x| y)`.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n # fn opt() -> Option<&'static str> { Some(\"42\") }\n # fn res() -> Result<&'static str, &'static str> { Ok(\"42\") }\n let _ = opt().and_then(|s| Some(s.len()));\n let _ = res().and_then(|s| if s.len() == 42 { Ok(10) } else { Ok(20) });\n let _ = res().or_else(|s| if s.len() == 42 { Err(10) } else { Err(20) });\n ```\n\n The correct use would be:\n\n ```rust\n # fn opt() -> Option<&'static str> { Some(\"42\") }\n # fn res() -> Result<&'static str, &'static str> { Ok(\"42\") }\n let _ = opt().map(|s| s.len());\n let _ = res().map(|s| if s.len() == 42 { 10 } else { 20 });\n let _ = res().map_err(|s| if s.len() == 42 { 10 } else { 20 });\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "filter_next", - "id_span": { - "path": "src/methods/mod.rs", - "line": 377 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `_.filter(_).next()`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.find(_)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let vec = vec![1];\n vec.iter().filter(|x| **x == 0).next();\n ```\n Could be written as\n ```rust\n # let vec = vec![1];\n vec.iter().find(|x| **x == 0);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "skip_while_next", - "id_span": { - "path": "src/methods/mod.rs", - "line": 400 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `_.skip_while(condition).next()`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.find(!condition)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let vec = vec![1];\n vec.iter().skip_while(|x| **x == 0).next();\n ```\n Could be written as\n ```rust\n # let vec = vec![1];\n vec.iter().find(|x| **x != 0);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "map_flatten", - "id_span": { - "path": "src/methods/mod.rs", - "line": 423 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for usage of `_.map(_).flatten(_)`,\n **Why is this bad?** Readability, this can be written more concisely as\n `_.flat_map(_)`\n\n **Known problems:**\n\n **Example:**\n ```rust\n let vec = vec![vec![1]];\n\n // Bad\n vec.iter().map(|x| x.iter()).flatten();\n\n // Good\n vec.iter().flat_map(|x| x.iter());\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "filter_map", - "id_span": { - "path": "src/methods/mod.rs", - "line": 452 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for usage of `_.filter(_).map(_)`, `_.filter(_).flat_map(_)`, `_.filter_map(_).flat_map(_)` and similar.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.filter_map(_)`.\n\n **Known problems:** Often requires a condition + Option/Iterator creation\n inside the closure.\n\n **Example:**\n ```rust\n let vec = vec![1];\n\n // Bad\n vec.iter().filter(|x| **x == 0).map(|x| *x * 2);\n\n // Good\n vec.iter().filter_map(|x| if *x == 0 {\n Some(*x * 2)\n } else {\n None\n });\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "manual_filter_map", - "id_span": { - "path": "src/methods/mod.rs", - "line": 478 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `_.filter(_).map(_)` that can be written more simply as `filter_map(_)`.\n\n **Why is this bad?** Redundant code in the `filter` and `map` operations is poor style and\n less performant.\n\n **Known problems:** None.\n\n **Example:**\n Bad:\n ```rust\n (0_i32..10)\n .filter(|n| n.checked_add(1).is_some())\n .map(|n| n.checked_add(1).unwrap());\n ```\n\n Good:\n ```rust\n (0_i32..10).filter_map(|n| n.checked_add(1));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "manual_find_map", - "id_span": { - "path": "src/methods/mod.rs", - "line": 504 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `_.find(_).map(_)` that can be written more simply as `find_map(_)`.\n\n **Why is this bad?** Redundant code in the `find` and `map` operations is poor style and\n less performant.\n\n **Known problems:** None.\n\n **Example:**\n Bad:\n ```rust\n (0_i32..10)\n .find(|n| n.checked_add(1).is_some())\n .map(|n| n.checked_add(1).unwrap());\n ```\n\n Good:\n ```rust\n (0_i32..10).find_map(|n| n.checked_add(1));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "filter_map_next", - "id_span": { - "path": "src/methods/mod.rs", - "line": 526 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for usage of `_.filter_map(_).next()`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.find_map(_)`.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n (0..3).filter_map(|x| if x == 2 { Some(x) } else { None }).next();\n ```\n Can be written as\n\n ```rust\n (0..3).find_map(|x| if x == 2 { Some(x) } else { None });\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "flat_map_identity", - "id_span": { - "path": "src/methods/mod.rs", - "line": 548 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `flat_map(|x| x)`.\n **Why is this bad?** Readability, this can be written more concisely by using `flatten`.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n # let iter = vec![vec![0]].into_iter();\n iter.flat_map(|x| x);\n ```\n Can be written as\n ```rust\n # let iter = vec![vec![0]].into_iter();\n iter.flatten();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "search_is_some", - "id_span": { - "path": "src/methods/mod.rs", - "line": 572 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for an iterator or string search (such as `find()`, `position()`, or `rposition()`) followed by a call to `is_some()`.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.any(_)` or `_.contains(_)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let vec = vec![1];\n vec.iter().find(|x| **x == 0).is_some();\n ```\n Could be written as\n ```rust\n # let vec = vec![1];\n vec.iter().any(|x| *x == 0);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "chars_next_cmp", - "id_span": { - "path": "src/methods/mod.rs", - "line": 596 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usage of `.chars().next()` on a `str` to check if it starts with a given char.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.starts_with(_)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let name = \"foo\";\n if name.chars().next() == Some('_') {};\n ```\n Could be written as\n ```rust\n let name = \"foo\";\n if name.starts_with('_') {};\n ```\n", - "applicability": null - }, - { - "id": "or_fun_call", - "id_span": { - "path": "src/methods/mod.rs", - "line": 627 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`, etc., and suggests to use `or_else`, `unwrap_or_else`, etc., or\n `unwrap_or_default` instead.\n\n **Why is this bad?** The function will always be called and potentially\n allocate an object acting as the default.\n\n **Known problems:** If the function has side-effects, not calling it will\n change the semantic of the program, but you shouldn't rely on that anyway.\n\n **Example:**\n ```rust\n # let foo = Some(String::new());\n foo.unwrap_or(String::new());\n ```\n this can instead be written:\n ```rust\n # let foo = Some(String::new());\n foo.unwrap_or_else(String::new);\n ```\n or\n ```rust\n # let foo = Some(String::new());\n foo.unwrap_or_default();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "HasPlaceholders" - } - }, - { - "id": "expect_fun_call", - "id_span": { - "path": "src/methods/mod.rs", - "line": 662 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`, etc., and suggests to use `unwrap_or_else` instead\n\n **Why is this bad?** The function will always be called.\n\n **Known problems:** If the function has side-effects, not calling it will\n change the semantics of the program, but you shouldn't rely on that anyway.\n\n **Example:**\n ```rust\n # let foo = Some(String::new());\n # let err_code = \"418\";\n # let err_msg = \"I'm a teapot\";\n foo.expect(&format!(\"Err {}: {}\", err_code, err_msg));\n ```\n or\n ```rust\n # let foo = Some(String::new());\n # let err_code = \"418\";\n # let err_msg = \"I'm a teapot\";\n foo.expect(format!(\"Err {}: {}\", err_code, err_msg).as_str());\n ```\n this can instead be written:\n ```rust\n # let foo = Some(String::new());\n # let err_code = \"418\";\n # let err_msg = \"I'm a teapot\";\n foo.unwrap_or_else(|| panic!(\"Err {}: {}\", err_code, err_msg));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "clone_on_copy", - "id_span": { - "path": "src/methods/mod.rs", - "line": 679 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `.clone()` on a `Copy` type.\n **Why is this bad?** The only reason `Copy` types implement `Clone` is for\n generics, not for using the `clone` method on a concrete type.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n 42u64.clone();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "clone_on_ref_ptr", - "id_span": { - "path": "src/methods/mod.rs", - "line": 704 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of `.clone()` on a ref-counted pointer, (`Rc`, `Arc`, `rc::Weak`, or `sync::Weak`), and suggests calling Clone via unified\n function syntax instead (e.g., `Rc::clone(foo)`).\n\n **Why is this bad?** Calling '.clone()' on an Rc, Arc, or Weak\n can obscure the fact that only the pointer is being cloned, not the underlying\n data.\n\n **Example:**\n ```rust\n # use std::rc::Rc;\n let x = Rc::new(1);\n\n // Bad\n x.clone();\n\n // Good\n Rc::clone(&x);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "clone_double_ref", - "id_span": { - "path": "src/methods/mod.rs", - "line": 726 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for usage of `.clone()` on an `&&T`.\n **Why is this bad?** Cloning an `&&T` copies the inner `&T`, instead of\n cloning the underlying `T`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn main() {\n let x = vec![1];\n let y = &&x;\n let z = y.clone();\n println!(\"{:p} {:p}\", *y, z); // prints out the same pointer\n }\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "inefficient_to_string", - "id_span": { - "path": "src/methods/mod.rs", - "line": 749 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for usage of `.to_string()` on an `&&T` where `T` implements `ToString` directly (like `&&str` or `&&String`).\n\n **Why is this bad?** This bypasses the specialized implementation of\n `ToString` and instead goes through the more expensive string formatting\n facilities.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Generic implementation for `T: Display` is used (slow)\n [\"foo\", \"bar\"].iter().map(|s| s.to_string());\n\n // OK, the specialized impl is used\n [\"foo\", \"bar\"].iter().map(|&s| s.to_string());\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "new_ret_no_self", - "id_span": { - "path": "src/methods/mod.rs", - "line": 810 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `new` not returning a type that contains `Self`.\n **Why is this bad?** As a convention, `new` methods are used to make a new\n instance of a type.\n\n **Known problems:** None.\n\n **Example:**\n In an impl block:\n ```rust\n # struct Foo;\n # struct NotAFoo;\n impl Foo {\n fn new() -> NotAFoo {\n # NotAFoo\n }\n }\n ```\n\n ```rust\n # struct Foo;\n struct Bar(Foo);\n impl Foo {\n // Bad. The type name must contain `Self`\n fn new() -> Bar {\n # Bar(Foo)\n }\n }\n ```\n\n ```rust\n # struct Foo;\n # struct FooError;\n impl Foo {\n // Good. Return type contains `Self`\n fn new() -> Result {\n # Ok(Foo)\n }\n }\n ```\n\n Or in a trait definition:\n ```rust\n pub trait Trait {\n // Bad. The type name must contain `Self`\n fn new();\n }\n ```\n\n ```rust\n pub trait Trait {\n // Good. Return type contains `Self`\n fn new() -> Self;\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "single_char_pattern", - "id_span": { - "path": "src/methods/mod.rs", - "line": 831 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for string methods that receive a single-character `str` as an argument, e.g., `_.split(\"x\")`.\n\n **Why is this bad?** Performing these methods using a `char` is faster than\n using a `str`.\n\n **Known problems:** Does not catch multi-byte unicode characters.\n\n **Example:**\n ```rust,ignore\n // Bad\n _.split(\"x\");\n\n // Good\n _.split('x');\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "iterator_step_by_zero", - "id_span": { - "path": "src/methods/mod.rs", - "line": 850 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for calling `.step_by(0)` on iterators which panics.\n **Why is this bad?** This very much looks like an oversight. Use `panic!()` instead if you\n actually intend to panic.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,should_panic\n for x in (0..100).step_by(0) {\n //..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "iter_nth_zero", - "id_span": { - "path": "src/methods/mod.rs", - "line": 878 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for the use of `iter.nth(0)`.\n **Why is this bad?** `iter.next()` is equivalent to\n `iter.nth(0)`, as they both consume the next element,\n but is more readable.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # use std::collections::HashSet;\n // Bad\n # let mut s = HashSet::new();\n # s.insert(1);\n let x = s.iter().nth(0);\n\n // Good\n # let mut s = HashSet::new();\n # s.insert(1);\n let x = s.iter().next();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "iter_nth", - "id_span": { - "path": "src/methods/mod.rs", - "line": 904 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for use of `.iter().nth()` (and the related `.iter_mut().nth()`) on standard library types with O(1) element access.\n\n **Why is this bad?** `.get()` and `.get_mut()` are more efficient and more\n readable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let some_vec = vec![0, 1, 2, 3];\n let bad_vec = some_vec.iter().nth(3);\n let bad_slice = &some_vec[..].iter().nth(3);\n ```\n The correct use would be:\n ```rust\n let some_vec = vec![0, 1, 2, 3];\n let bad_vec = some_vec.get(3);\n let bad_slice = &some_vec[..].get(3);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "iter_skip_next", - "id_span": { - "path": "src/methods/mod.rs", - "line": 928 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for use of `.skip(x).next()` on iterators.\n **Why is this bad?** `.nth(x)` is cleaner\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let some_vec = vec![0, 1, 2, 3];\n let bad_vec = some_vec.iter().skip(3).next();\n let bad_slice = &some_vec[..].iter().skip(3).next();\n ```\n The correct use would be:\n ```rust\n let some_vec = vec![0, 1, 2, 3];\n let bad_vec = some_vec.iter().nth(3);\n let bad_slice = &some_vec[..].iter().nth(3);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "get_unwrap", - "id_span": { - "path": "src/methods/mod.rs", - "line": 961 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for use of `.get().unwrap()` (or `.get_mut().unwrap`) on a standard library type which implements `Index`\n\n **Why is this bad?** Using the Index trait (`[]`) is more clear and more\n concise.\n\n **Known problems:** Not a replacement for error handling: Using either\n `.unwrap()` or the Index trait (`[]`) carries the risk of causing a `panic`\n if the value being accessed is `None`. If the use of `.get().unwrap()` is a\n temporary placeholder for dealing with the `Option` type, then this does\n not mitigate the need for error handling. If there is a chance that `.get()`\n will be `None` in your program, then it is advisable that the `None` case\n is handled in a future refactor instead of using `.unwrap()` or the Index\n trait.\n\n **Example:**\n ```rust\n let mut some_vec = vec![0, 1, 2, 3];\n let last = some_vec.get(3).unwrap();\n *some_vec.get_mut(0).unwrap() = 1;\n ```\n The correct use would be:\n ```rust\n let mut some_vec = vec![0, 1, 2, 3];\n let last = some_vec[3];\n some_vec[0] = 1;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "string_extend_chars", - "id_span": { - "path": "src/methods/mod.rs", - "line": 990 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for the use of `.extend(s.chars())` where s is a `&str` or `String`.\n\n **Why is this bad?** `.push_str(s)` is clearer\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let abc = \"abc\";\n let def = String::from(\"def\");\n let mut s = String::new();\n s.extend(abc.chars());\n s.extend(def.chars());\n ```\n The correct use would be:\n ```rust\n let abc = \"abc\";\n let def = String::from(\"def\");\n let mut s = String::new();\n s.push_str(abc);\n s.push_str(&def);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "iter_cloned_collect", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1013 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for the use of `.cloned().collect()` on slice to create a `Vec`.\n\n **Why is this bad?** `.to_vec()` is clearer\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let s = [1, 2, 3, 4, 5];\n let s2: Vec = s[..].iter().cloned().collect();\n ```\n The better use would be:\n ```rust\n let s = [1, 2, 3, 4, 5];\n let s2: Vec = s.to_vec();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "chars_last_cmp", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1037 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usage of `_.chars().last()` or `_.chars().next_back()` on a `str` to check if it ends with a given char.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.ends_with(_)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let name = \"_\";\n\n // Bad\n name.chars().last() == Some('_') || name.chars().next_back() == Some('-');\n\n // Good\n name.ends_with('_') || name.ends_with('-');\n ```\n", - "applicability": null - }, - { - "id": "useless_asref", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1062 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `.as_ref()` or `.as_mut()` where the types before and after the call are the same.\n\n **Why is this bad?** The call is unnecessary.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn do_stuff(x: &[i32]) {}\n let x: &[i32] = &[1, 2, 3, 4, 5];\n do_stuff(x.as_ref());\n ```\n The correct use would be:\n ```rust\n # fn do_stuff(x: &[i32]) {}\n let x: &[i32] = &[1, 2, 3, 4, 5];\n do_stuff(x);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unnecessary_fold", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1084 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for using `fold` when a more succinct alternative exists. Specifically, this checks for `fold`s which could be replaced by `any`, `all`,\n `sum` or `product`.\n\n **Why is this bad?** Readability.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let _ = (0..3).fold(false, |acc, x| acc || x > 2);\n ```\n This could be written as:\n ```rust\n let _ = (0..3).any(|x| x > 2);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unnecessary_filter_map", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1113 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for `filter_map` calls which could be replaced by `filter` or `map`. More specifically it checks if the closure provided is only performing one of the\n filter or map operations and suggests the appropriate option.\n\n **Why is this bad?** Complexity. The intent is also clearer if only a single\n operation is being performed.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n let _ = (0..3).filter_map(|x| if x > 2 { Some(x) } else { None });\n\n // As there is no transformation of the argument this could be written as:\n let _ = (0..3).filter(|&x| x > 2);\n ```\n\n ```rust\n let _ = (0..4).filter_map(|x| Some(x + 1));\n\n // As there is no conditional check on the argument this could be written as:\n let _ = (0..4).map(|x| x + 1);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "into_iter_on_ref", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1137 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `into_iter` calls on references which should be replaced by `iter` or `iter_mut`.\n\n **Why is this bad?** Readability. Calling `into_iter` on a reference will not move out its\n content into the resulting iterator, which is confusing. It is better just call `iter` or\n `iter_mut` directly.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n // Bad\n let _ = (&vec![3, 4, 5]).into_iter();\n\n // Good\n let _ = (&vec![3, 4, 5]).iter();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "suspicious_map", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1156 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for calls to `map` followed by a `count`.\n **Why is this bad?** It looks suspicious. Maybe `map` was confused with `filter`.\n If the `map` call is intentional, this should be rewritten. Or, if you intend to\n drive the iterator to completion, you can just use `for_each` instead.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n let _ = (0..3).map(|x| x + 2).count();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "uninit_assumed_init", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1188 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `MaybeUninit::uninit().assume_init()`.\n **Why is this bad?** For most types, this is undefined behavior.\n\n **Known problems:** For now, we accept empty tuples and tuples / arrays\n of `MaybeUninit`. There may be other types that allow uninitialized\n data, but those are not yet rigorously defined.\n\n **Example:**\n\n ```rust\n // Beware the UB\n use std::mem::MaybeUninit;\n\n let _: usize = unsafe { MaybeUninit::uninit().assume_init() };\n ```\n\n Note that the following is OK:\n\n ```rust\n use std::mem::MaybeUninit;\n\n let _: [MaybeUninit; 5] = unsafe {\n MaybeUninit::uninit().assume_init()\n };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "manual_saturating_arithmetic", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1215 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`.\n **Why is this bad?** These can be written simply with `saturating_add/sub` methods.\n\n **Example:**\n\n ```rust\n # let y: u32 = 0;\n # let x: u32 = 100;\n let add = x.checked_add(y).unwrap_or(u32::MAX);\n let sub = x.checked_sub(y).unwrap_or(u32::MIN);\n ```\n\n can be written using dedicated methods for saturating addition/subtraction as:\n\n ```rust\n # let y: u32 = 0;\n # let x: u32 = 100;\n let add = x.saturating_add(y);\n let sub = x.saturating_sub(y);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "zst_offset", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1232 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to zero-sized types\n\n **Why is this bad?** This is a no-op, and likely unintended\n\n **Known problems:** None\n\n **Example:**\n ```rust\n unsafe { (&() as *const ()).offset(1) };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "filetype_is_file", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1272 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for `FileType::is_file()`.\n **Why is this bad?** When people testing a file type with `FileType::is_file`\n they are testing whether a path is something they can get bytes from. But\n `is_file` doesn't cover special file types in unix-like systems, and doesn't cover\n symlink in windows. Using `!FileType::is_dir()` is a better way to that intention.\n\n **Example:**\n\n ```rust\n # || {\n let metadata = std::fs::metadata(\"foo.txt\")?;\n let filetype = metadata.file_type();\n\n if filetype.is_file() {\n // read file\n }\n # Ok::<_, std::io::Error>(())\n # };\n ```\n\n should be written as:\n\n ```rust\n # || {\n let metadata = std::fs::metadata(\"foo.txt\")?;\n let filetype = metadata.file_type();\n\n if !filetype.is_dir() {\n // read file\n }\n # Ok::<_, std::io::Error>(())\n # };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "option_as_ref_deref", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1297 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str).\n **Why is this bad?** Readability, this can be written more concisely as\n `_.as_deref()`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let opt = Some(\"\".to_string());\n opt.as_ref().map(String::as_str)\n # ;\n ```\n Can be written as\n ```rust\n # let opt = Some(\"\".to_string());\n opt.as_deref()\n # ;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "iter_next_slice", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1323 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usage of `iter().next()` on a Slice or an Array\n **Why is this bad?** These can be shortened into `.get()`\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let a = [1, 2, 3];\n # let b = vec![1, 2, 3];\n a[2..].iter().next();\n b.iter().next();\n ```\n should be written as:\n ```rust\n # let a = [1, 2, 3];\n # let b = vec![1, 2, 3];\n a.get(2);\n b.get(0);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "single_char_add_str", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1348 - }, - "group": "clippy::style", - "docs": " **What it does:** Warns when using `push_str`/`insert_str` with a single-character string literal where `push`/`insert` with a `char` would work fine.\n\n **Why is this bad?** It's less clear that we are pushing a single character.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n let mut string = String::new();\n string.insert_str(0, \"R\");\n string.push_str(\"R\");\n ```\n Could be written as\n ```rust\n let mut string = String::new();\n string.insert(0, 'R');\n string.push('R');\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unnecessary_lazy_evaluations", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1384 - }, - "group": "clippy::style", - "docs": " **What it does:** As the counterpart to `or_fun_call`, this lint looks for unnecessary lazily evaluated closures on `Option` and `Result`.\n\n This lint suggests changing the following functions, when eager evaluation results in\n simpler code:\n - `unwrap_or_else` to `unwrap_or`\n - `and_then` to `and`\n - `or_else` to `or`\n - `get_or_insert_with` to `get_or_insert`\n - `ok_or_else` to `ok_or`\n\n **Why is this bad?** Using eager evaluation is shorter and simpler in some cases.\n\n **Known problems:** It is possible, but not recommended for `Deref` and `Index` to have\n side effects. Eagerly evaluating them can change the semantics of the program.\n\n **Example:**\n\n ```rust\n // example code where clippy issues a warning\n let opt: Option = None;\n\n opt.unwrap_or_else(|| 42);\n ```\n Use instead:\n ```rust\n let opt: Option = None;\n\n opt.unwrap_or(42);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "map_collect_result_unit", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1405 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usage of `_.map(_).collect::()`.\n **Why is this bad?** Using `try_for_each` instead is more readable and idiomatic.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n (0..3).map(|t| Err(t)).collect::>();\n ```\n Use instead:\n ```rust\n (0..3).try_for_each(|t| Err(t));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "from_iter_instead_of_collect", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1438 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `from_iter()` function calls on types that implement the `FromIterator` trait.\n\n **Why is this bad?** It is recommended style to use collect. See\n [FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html)\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n use std::iter::FromIterator;\n\n let five_fives = std::iter::repeat(5).take(5);\n\n let v = Vec::from_iter(five_fives);\n\n assert_eq!(v, vec![5, 5, 5, 5, 5]);\n ```\n Use instead:\n ```rust\n let five_fives = std::iter::repeat(5).take(5);\n\n let v: Vec = five_fives.collect();\n\n assert_eq!(v, vec![5, 5, 5, 5, 5]);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "inspect_for_each", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1468 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `inspect().for_each()`.\n **Why is this bad?** It is the same as performing the computation\n inside `inspect` at the beginning of the closure in `for_each`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n [1,2,3,4,5].iter()\n .inspect(|&x| println!(\"inspect the number: {}\", x))\n .for_each(|&x| {\n assert!(x >= 0);\n });\n ```\n Can be written as\n ```rust\n [1,2,3,4,5].iter()\n .for_each(|&x| {\n println!(\"inspect the number: {}\", x);\n assert!(x >= 0);\n });\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "filter_map_identity", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1491 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `filter_map(|x| x)`.\n **Why is this bad?** Readability, this can be written more concisely by using `flatten`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # let iter = vec![Some(1)].into_iter();\n iter.filter_map(|x| x);\n ```\n Use instead:\n ```rust\n # let iter = vec![Some(1)].into_iter();\n iter.flatten();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "bytes_nth", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1513 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for the use of `.bytes().nth()`.\n **Why is this bad?** `.as_bytes().get()` is more efficient and more\n readable.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let _ = \"Hello\".bytes().nth(3);\n\n // Good\n let _ = \"Hello\".as_bytes().get(3);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "implicit_clone", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1539 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for the usage of `_.to_owned()`, `vec.to_vec()`, or similar when calling `_.clone()` would be clearer.\n **Why is this bad?** These methods do the same thing as `_.clone()` but may be confusing as\n to why we are calling `to_vec` on something that is already a `Vec` or calling `to_owned` on something that is already owned.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let a = vec![1, 2, 3];\n let b = a.to_vec();\n let c = a.to_owned();\n ```\n Use instead:\n ```rust\n let a = vec![1, 2, 3];\n let b = a.clone();\n let c = a.clone();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "iter_count", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1565 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for the use of `.iter().count()`.\n **Why is this bad?** `.len()` is more efficient and more\n readable.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let some_vec = vec![0, 1, 2, 3];\n let _ = some_vec.iter().count();\n let _ = &some_vec[..].iter().count();\n\n // Good\n let some_vec = vec![0, 1, 2, 3];\n let _ = some_vec.len();\n let _ = &some_vec[..].len();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "min_max", - "id_span": { - "path": "src/minmax.rs", - "line": 28 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for expressions where `std::cmp::min` and `max` are used to clamp values, but switched so that the result is constant.\n\n **Why is this bad?** This is in all probability not the intended outcome. At\n the least it hurts readability of the code.\n\n **Known problems:** None\n\n **Example:**\n ```ignore\n min(0, max(100, x))\n ```\n or\n ```ignore\n x.max(100).min(0)\n ```\n It will always be equal to `0`. Probably the author meant to clamp the value\n between 0 and 100, but has erroneously swapped `min` and `max`.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "toplevel_ref_arg", - "id_span": { - "path": "src/misc.rs", - "line": 53 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for function arguments and let bindings denoted as `ref`.\n\n **Why is this bad?** The `ref` declaration makes the function take an owned\n value, but turns the argument into a reference (which means that the value\n is destroyed when exiting the function). This adds not much value: either\n take a reference type, or take an owned value and create references in the\n body.\n\n For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The\n type of `x` is more obvious with the former.\n\n **Known problems:** If the argument is dereferenced within the function,\n removing the `ref` will lead to errors. This can be fixed by removing the\n dereferences, e.g., changing `*x` to `x` within the function.\n\n **Example:**\n ```rust,ignore\n // Bad\n fn foo(ref x: u8) -> bool {\n true\n }\n\n // Good\n fn foo(x: &u8) -> bool {\n true\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "cmp_nan", - "id_span": { - "path": "src/misc.rs", - "line": 76 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for comparisons to NaN.\n **Why is this bad?** NaN does not compare meaningfully to anything – not\n even itself – so those comparisons are simply wrong.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = 1.0;\n\n // Bad\n if x == f32::NAN { }\n\n // Good\n if x.is_nan() { }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "float_cmp", - "id_span": { - "path": "src/misc.rs", - "line": 109 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for (in-)equality comparisons on floating-point values (apart from zero), except in functions called `*eq*` (which probably\n implement equality for a type involving floats).\n\n **Why is this bad?** Floating point calculations are usually imprecise, so\n asking if two values are *exactly* equal is asking for trouble. For a good\n guide on what to do, see [the floating point\n guide](http://www.floating-point-gui.de/errors/comparison).\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = 1.2331f64;\n let y = 1.2332f64;\n\n // Bad\n if y == 1.23f64 { }\n if y != x {} // where both are floats\n\n // Good\n let error_margin = f64::EPSILON; // Use an epsilon for comparison\n // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.\n // let error_margin = std::f64::EPSILON;\n if (y - 1.23f64).abs() < error_margin { }\n if (y - x).abs() > error_margin { }\n ```\n", - "applicability": null - }, - { - "id": "cmp_owned", - "id_span": { - "path": "src/misc.rs", - "line": 136 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for conversions to owned values just for the sake of a comparison.\n\n **Why is this bad?** The comparison can operate on a reference, so creating\n an owned value effectively throws it away directly afterwards, which is\n needlessly consuming code and heap space.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = \"foo\";\n # let y = String::from(\"foo\");\n if x.to_owned() == y {}\n ```\n Could be written as\n ```rust\n # let x = \"foo\";\n # let y = String::from(\"foo\");\n if x == y {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "modulo_one", - "id_span": { - "path": "src/misc.rs", - "line": 159 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for getting the remainder of a division by one or minus one.\n\n **Why is this bad?** The result for a divisor of one can only ever be zero; for\n minus one it can cause panic/overflow (if the left operand is the minimal value of\n the respective integer type) or results in zero. No one will write such code\n deliberately, unless trying to win an Underhanded Rust Contest. Even for that\n contest, it's probably a bad idea. Use something more underhanded.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = 1;\n let a = x % 1;\n let a = x % -1;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "used_underscore_binding", - "id_span": { - "path": "src/misc.rs", - "line": 181 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for the use of bindings with a single leading underscore.\n\n **Why is this bad?** A single leading underscore is usually used to indicate\n that a binding will not be used. Using such a binding breaks this\n expectation.\n\n **Known problems:** The lint does not work properly with desugaring and\n macro, it has been allowed in the mean time.\n\n **Example:**\n ```rust\n let _x = 0;\n let y = _x + 1; // Here we are using `_x`, even though it has a leading\n // underscore. We should rename `_x` to `x`\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "short_circuit_statement", - "id_span": { - "path": "src/misc.rs", - "line": 201 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for the use of short circuit boolean conditions as a\n statement.\n\n **Why is this bad?** Using a short circuit boolean condition as a statement\n may hide the fact that the second part is executed or not depending on the\n outcome of the first part.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n f() && g(); // We should write `if f() { g(); }`.\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "zero_ptr", - "id_span": { - "path": "src/misc.rs", - "line": 223 - }, - "group": "clippy::style", - "docs": " **What it does:** Catch casts from `0` to some pointer type\n **Why is this bad?** This generally means `null` and is better expressed as\n {`std`, `core`}`::ptr::`{`null`, `null_mut`}.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let a = 0 as *const u32;\n\n // Good\n let a = std::ptr::null::();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "float_cmp_const", - "id_span": { - "path": "src/misc.rs", - "line": 254 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for (in-)equality comparisons on floating-point value and constant, except in functions called `*eq*` (which probably\n implement equality for a type involving floats).\n\n **Why is this bad?** Floating point calculations are usually imprecise, so\n asking if two values are *exactly* equal is asking for trouble. For a good\n guide on what to do, see [the floating point\n guide](http://www.floating-point-gui.de/errors/comparison).\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x: f64 = 1.0;\n const ONE: f64 = 1.00;\n\n // Bad\n if x == ONE { } // where both are floats\n\n // Good\n let error_margin = f64::EPSILON; // Use an epsilon for comparison\n // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.\n // let error_margin = std::f64::EPSILON;\n if (x - ONE).abs() < error_margin { }\n ```\n", - "applicability": null - }, - { - "id": "unneeded_field_pattern", - "id_span": { - "path": "src/misc_early.rs", - "line": 44 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for structure field patterns bound to wildcards.\n **Why is this bad?** Using `..` instead is shorter and leaves the focus on\n the fields that are actually bound.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct Foo {\n # a: i32,\n # b: i32,\n # c: i32,\n # }\n let f = Foo { a: 0, b: 0, c: 0 };\n\n // Bad\n match f {\n Foo { a: _, b: 0, .. } => {},\n Foo { a: _, b: _, c: _ } => {},\n }\n\n // Good\n match f {\n Foo { b: 0, .. } => {},\n Foo { .. } => {},\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "duplicate_underscore_argument", - "id_span": { - "path": "src/misc_early.rs", - "line": 65 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for function arguments having the similar names differing by an underscore.\n\n **Why is this bad?** It affects code readability.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n fn foo(a: i32, _a: i32) {}\n\n // Good\n fn bar(a: i32, _b: i32) {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "double_neg", - "id_span": { - "path": "src/misc_early.rs", - "line": 83 - }, - "group": "clippy::style", - "docs": " **What it does:** Detects expressions of the form `--x`.\n **Why is this bad?** It can mislead C/C++ programmers to think `x` was\n decremented.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let mut x = 3;\n --x;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mixed_case_hex_literals", - "id_span": { - "path": "src/misc_early.rs", - "line": 104 - }, - "group": "clippy::style", - "docs": " **What it does:** Warns on hexadecimal literals with mixed-case letter digits.\n\n **Why is this bad?** It looks confusing.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n let y = 0x1a9BAcD;\n\n // Good\n let y = 0x1A9BACD;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unseparated_literal_suffix", - "id_span": { - "path": "src/misc_early.rs", - "line": 125 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Warns if literal suffixes are not separated by an underscore.\n\n **Why is this bad?** It is much less readable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n let y = 123832i32;\n\n // Good\n let y = 123832_i32;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "zero_prefixed_literal", - "id_span": { - "path": "src/misc_early.rs", - "line": 163 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Warns if an integral constant literal starts with `0`.\n **Why is this bad?** In some languages (including the infamous C language\n and most of its\n family), this marks an octal constant. In Rust however, this is a decimal\n constant. This could\n be confusing for both the writer and a reader of the constant.\n\n **Known problems:** None.\n\n **Example:**\n\n In Rust:\n ```rust\n fn main() {\n let a = 0123;\n println!(\"{}\", a);\n }\n ```\n\n prints `123`, while in C:\n\n ```c\n #include \n\n int main() {\n int a = 0123;\n printf(\"%d\\n\", a);\n }\n ```\n\n prints `83` (as `83 == 0o123` while `123 == 0o173`).\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "builtin_type_shadow", - "id_span": { - "path": "src/misc_early.rs", - "line": 184 - }, - "group": "clippy::style", - "docs": " **What it does:** Warns if a generic shadows a built-in type.\n **Why is this bad?** This gives surprising type errors.\n\n **Known problems:** None.\n\n **Example:**\n\n ```ignore\n impl Foo {\n fn impl_func(&self) -> u32 {\n 42\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "redundant_pattern", - "id_span": { - "path": "src/misc_early.rs", - "line": 213 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for patterns in the form `name @ _`.\n **Why is this bad?** It's almost always more readable to just use direct\n bindings.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let v = Some(\"abc\");\n\n // Bad\n match v {\n Some(x) => (),\n y @ _ => (),\n }\n\n // Good\n match v {\n Some(x) => (),\n y => (),\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unneeded_wildcard_pattern", - "id_span": { - "path": "src/misc_early.rs", - "line": 247 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`).\n\n _NOTE_: While `_, ..` means there is at least one element left, `..`\n means there are 0 or more elements left. This can make a difference\n when refactoring, but shouldn't result in errors in the refactored code,\n since the wildcard pattern isn't used anyway.\n **Why is this bad?** The wildcard pattern is unneeded as the rest pattern\n can match that element as well.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct TupleStruct(u32, u32, u32);\n # let t = TupleStruct(1, 2, 3);\n // Bad\n match t {\n TupleStruct(0, .., _) => (),\n _ => (),\n }\n\n // Good\n match t {\n TupleStruct(0, ..) => (),\n _ => (),\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "missing_const_for_fn", - "id_span": { - "path": "src/missing_const_for_fn.rs", - "line": 72 - }, - "group": "clippy::nursery", - "docs": " **What it does:**\n Suggests the use of `const` in functions and methods where possible.\n\n **Why is this bad?**\n\n Not having the function const prevents callers of the function from being const as well.\n\n **Known problems:**\n\n Const functions are currently still being worked on, with some features only being available\n on nightly. This lint does not consider all edge cases currently and the suggestions may be\n incorrect if you are using this lint on stable.\n\n Also, the lint only runs one pass over the code. Consider these two non-const functions:\n\n ```rust\n fn a() -> i32 {\n 0\n }\n fn b() -> i32 {\n a()\n }\n ```\n\n When running Clippy, the lint will only suggest to make `a` const, because `b` at this time\n can't be const as it calls a non-const function. Making `a` const and running Clippy again,\n will suggest to make `b` const, too.\n\n **Example:**\n\n ```rust\n # struct Foo {\n # random_number: usize,\n # }\n # impl Foo {\n fn new() -> Self {\n Self { random_number: 42 }\n }\n # }\n ```\n\n Could be a const fn:\n\n ```rust\n # struct Foo {\n # random_number: usize,\n # }\n # impl Foo {\n const fn new() -> Self {\n Self { random_number: 42 }\n }\n # }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "missing_docs_in_private_items", - "id_span": { - "path": "src/missing_doc.rs", - "line": 29 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Warns if there is missing doc for any documentable item (public or private).\n\n **Why is this bad?** Doc is good. *rustc* has a `MISSING_DOCS`\n allowed-by-default lint for\n public members, but has no way to enforce documentation of private items.\n This lint fixes that.\n\n **Known problems:** None.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "missing_inline_in_public_items", - "id_span": { - "path": "src/missing_inline.rs", - "line": 55 - }, - "group": "clippy::restriction", - "docs": " **What it does:** it lints if an exported function, method, trait method with default impl, or trait method impl is not `#[inline]`.\n\n **Why is this bad?** In general, it is not. Functions can be inlined across\n crates when that's profitable as long as any form of LTO is used. When LTO is disabled,\n functions that are not `#[inline]` cannot be inlined across crates. Certain types of crates\n might intend for most of the methods in their public API to be able to be inlined across\n crates even when LTO is disabled. For these types of crates, enabling this lint might make\n sense. It allows the crate to require all exported methods to be `#[inline]` by default, and\n then opt out for specific methods where this might not make sense.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n pub fn foo() {} // missing #[inline]\n fn ok() {} // ok\n #[inline] pub fn bar() {} // ok\n #[inline(always)] pub fn baz() {} // ok\n\n pub trait Bar {\n fn bar(); // ok\n fn def_bar() {} // missing #[inline]\n }\n\n struct Baz;\n impl Baz {\n fn private() {} // ok\n }\n\n impl Bar for Baz {\n fn bar() {} // ok - Baz is not exported\n }\n\n pub struct PubBaz;\n impl PubBaz {\n fn private() {} // ok\n pub fn not_ptrivate() {} // missing #[inline]\n }\n\n impl Bar for PubBaz {\n fn bar() {} // missing #[inline]\n fn def_bar() {} // missing #[inline]\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "modulo_arithmetic", - "id_span": { - "path": "src/modulo_arithmetic.rs", - "line": 26 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for modulo arithmetic.\n **Why is this bad?** The results of modulo (%) operation might differ\n depending on the language, when negative numbers are involved.\n If you interop with different languages it might be beneficial\n to double check all places that use modulo arithmetic.\n\n For example, in Rust `17 % -3 = 2`, but in Python `17 % -3 = -1`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = -17 % 3;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "multiple_crate_versions", - "id_span": { - "path": "src/multiple_crate_versions.rs", - "line": 32 - }, - "group": "clippy::cargo", - "docs": " **What it does:** Checks to see if multiple versions of a crate are being used.\n\n **Why is this bad?** This bloats the size of targets, and can lead to\n confusing error messages when structs or traits are used interchangeably\n between different versions of a crate.\n\n **Known problems:** Because this can be caused purely by the dependencies\n themselves, it's not always possible to fix this issue.\n\n **Example:**\n ```toml\n # This will pull in both winapi v0.3.x and v0.2.x, triggering a warning.\n [dependencies]\n ctrlc = \"=3.1.0\"\n ansi_term = \"=0.11.0\"\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mutable_key_type", - "id_span": { - "path": "src/mut_key.rs", - "line": 50 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for sets/maps with mutable key types.\n **Why is this bad?** All of `HashMap`, `HashSet`, `BTreeMap` and\n `BtreeSet` rely on either the hash or the order of keys be unchanging,\n so having types with interior mutability is a bad idea.\n\n **Known problems:** It's correct to use a struct, that contains interior mutability\n as a key, when its `Hash` implementation doesn't access any of the interior mutable types.\n However, this lint is unable to recognize this, so it causes a false positive in theses cases.\n The `bytes` crate is a great example of this.\n\n **Example:**\n ```rust\n use std::cmp::{PartialEq, Eq};\n use std::collections::HashSet;\n use std::hash::{Hash, Hasher};\n use std::sync::atomic::AtomicUsize;\n# #[allow(unused)]\n\n struct Bad(AtomicUsize);\n impl PartialEq for Bad {\n fn eq(&self, rhs: &Self) -> bool {\n ..\n ; unimplemented!();\n }\n }\n\n impl Eq for Bad {}\n\n impl Hash for Bad {\n fn hash(&self, h: &mut H) {\n ..\n ; unimplemented!();\n }\n }\n\n fn main() {\n let _: HashSet = HashSet::new();\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mut_mut", - "id_span": { - "path": "src/mut_mut.rs", - "line": 24 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for instances of `mut mut` references.\n **Why is this bad?** Multiple `mut`s don't add anything meaningful to the\n source. This is either a copy'n'paste error, or it shows a fundamental\n misunderstanding of references.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let mut y = 1;\n let x = &mut &mut y;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mut_mutex_lock", - "id_span": { - "path": "src/mut_mutex_lock.rs", - "line": 40 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `&mut Mutex::lock` calls\n **Why is this bad?** `Mutex::lock` is less efficient than\n calling `Mutex::get_mut`. In addition you also have a statically\n guarantee that the mutex isn't locked, instead of just a runtime\n guarantee.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n use std::sync::{Arc, Mutex};\n\n let mut value_rc = Arc::new(Mutex::new(42_u8));\n let value_mutex = Arc::get_mut(&mut value_rc).unwrap();\n\n let mut value = value_mutex.lock().unwrap();\n *value += 1;\n ```\n Use instead:\n ```rust\n use std::sync::{Arc, Mutex};\n\n let mut value_rc = Arc::new(Mutex::new(42_u8));\n let value_mutex = Arc::get_mut(&mut value_rc).unwrap();\n\n let value = value_mutex.get_mut().unwrap();\n *value += 1;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "unnecessary_mut_passed", - "id_span": { - "path": "src/mut_reference.rs", - "line": 25 - }, - "group": "clippy::style", - "docs": " **What it does:** Detects passing a mutable reference to a function that only requires an immutable reference.\n\n **Why is this bad?** The mutable reference rules out all other references to\n the value. Also the code misleads about the intent of the call site.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n // Bad\n my_vec.push(&mut value)\n\n // Good\n my_vec.push(&value)\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "debug_assert_with_mut_call", - "id_span": { - "path": "src/mutable_debug_assertion.rs", - "line": 28 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for function/method calls with a mutable parameter in `debug_assert!`, `debug_assert_eq!` and `debug_assert_ne!` macros.\n\n **Why is this bad?** In release builds `debug_assert!` macros are optimized out by the\n compiler.\n Therefore mutating something in a `debug_assert!` macro results in different behaviour\n between a release and debug build.\n\n **Known problems:** None\n\n **Example:**\n ```rust,ignore\n debug_assert_eq!(vec![3].pop(), Some(3));\n // or\n fn take_a_mut_parameter(_: &mut u32) -> bool { unimplemented!() }\n debug_assert!(take_a_mut_parameter(&mut 5));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mutex_atomic", - "id_span": { - "path": "src/mutex_atomic.rs", - "line": 34 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for usages of `Mutex` where an atomic will do.\n **Why is this bad?** Using a mutex just to make access to a plain bool or\n reference sequential is shooting flies with cannons.\n `std::sync::atomic::AtomicBool` and `std::sync::atomic::AtomicPtr` are leaner and\n faster.\n\n **Known problems:** This lint cannot detect if the mutex is actually used\n for waiting before a critical section.\n\n **Example:**\n ```rust\n # let y = true;\n\n // Bad\n # use std::sync::Mutex;\n let x = Mutex::new(&y);\n\n // Good\n # use std::sync::atomic::AtomicBool;\n let x = AtomicBool::new(y);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mutex_integer", - "id_span": { - "path": "src/mutex_atomic.rs", - "line": 59 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for usages of `Mutex` where `X` is an integral type.\n\n **Why is this bad?** Using a mutex just to make access to a plain integer\n sequential is\n shooting flies with cannons. `std::sync::atomic::AtomicUsize` is leaner and faster.\n\n **Known problems:** This lint cannot detect if the mutex is actually used\n for waiting before a critical section.\n\n **Example:**\n ```rust\n # use std::sync::Mutex;\n let x = Mutex::new(0usize);\n\n // Good\n # use std::sync::atomic::AtomicUsize;\n let x = AtomicUsize::new(0usize);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "needless_arbitrary_self_type", - "id_span": { - "path": "src/needless_arbitrary_self_type.rs", - "line": 55 - }, - "group": "clippy::complexity", - "docs": " **What it does:** The lint checks for `self` in fn parameters that specify the `Self`-type explicitly\n **Why is this bad?** Increases the amount and decreases the readability of code\n\n **Known problems:** None\n\n **Example:**\n ```rust\n enum ValType {\n I32,\n I64,\n F32,\n F64,\n }\n\n impl ValType {\n pub fn bytes(self: Self) -> usize {\n match self {\n Self::I32 | Self::F32 => 4,\n Self::I64 | Self::F64 => 8,\n }\n }\n }\n ```\n\n Could be rewritten as\n\n ```rust\n enum ValType {\n I32,\n I64,\n F32,\n F64,\n }\n\n impl ValType {\n pub fn bytes(self) -> usize {\n match self {\n Self::I32 | Self::F32 => 4,\n Self::I64 | Self::F64 => 8,\n }\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "needless_bool", - "id_span": { - "path": "src/needless_bool.rs", - "line": 38 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for expressions of the form `if c { true } else { false }` (or vice versa) and suggests using the condition directly.\n\n **Why is this bad?** Redundant code.\n\n **Known problems:** Maybe false positives: Sometimes, the two branches are\n painstakingly documented (which we, of course, do not detect), so they *may*\n have some value. Even then, the documentation can be rewritten to match the\n shorter code.\n\n **Example:**\n ```rust,ignore\n if x {\n false\n } else {\n true\n }\n ```\n Could be written as\n ```rust,ignore\n !x\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "bool_comparison", - "id_span": { - "path": "src/needless_bool.rs", - "line": 62 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for expressions of the form `x == true`, `x != true` and order comparisons such as `x < true` (or vice versa) and\n suggest using the variable directly.\n\n **Why is this bad?** Unnecessary code.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n if x == true {}\n if y == false {}\n ```\n use `x` directly:\n ```rust,ignore\n if x {}\n if !y {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "needless_borrow", - "id_span": { - "path": "src/needless_borrow.rs", - "line": 32 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for address of operations (`&`) that are going to be dereferenced immediately by the compiler.\n\n **Why is this bad?** Suggests that the receiver of the expression borrows\n the expression.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n let x: &i32 = &&&&&&5;\n\n // Good\n let x: &i32 = &5;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "needless_borrowed_reference", - "id_span": { - "path": "src/needless_borrowed_ref.rs", - "line": 48 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for useless borrowed references.\n **Why is this bad?** It is mostly useless and make the code look more\n complex than it\n actually is.\n\n **Known problems:** It seems that the `&ref` pattern is sometimes useful.\n For instance in the following snippet:\n ```rust,ignore\n enum Animal {\n Cat(u64),\n Dog(u64),\n }\n\n fn foo(a: &Animal, b: &Animal) {\n match (a, b) {\n (&Animal::Cat(v), k) | (k, &Animal::Cat(v)) => (), // lifetime mismatch error\n (&Animal::Dog(ref c), &Animal::Dog(_)) => ()\n }\n }\n ```\n There is a lifetime mismatch error for `k` (indeed a and b have distinct\n lifetime).\n This can be fixed by using the `&ref` pattern.\n However, the code can also be fixed by much cleaner ways\n\n **Example:**\n ```rust\n let mut v = Vec::::new();\n let _ = v.iter_mut().filter(|&ref a| a.is_empty());\n ```\n This closure takes a reference on something that has been matched as a\n reference and\n de-referenced.\n As such, it could just be |a| a.is_empty()\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "needless_continue", - "id_span": { - "path": "src/needless_continue.rs", - "line": 114 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** The lint checks for `if`-statements appearing in loops that contain a `continue` statement in either their main blocks or their\n `else`-blocks, when omitting the `else`-block possibly with some\n rearrangement of code can make the code easier to understand.\n\n **Why is this bad?** Having explicit `else` blocks for `if` statements\n containing `continue` in their THEN branch adds unnecessary branching and\n nesting to the code. Having an else block containing just `continue` can\n also be better written by grouping the statements following the whole `if`\n statement within the THEN block and omitting the else block completely.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n # fn condition() -> bool { false }\n # fn update_condition() {}\n # let x = false;\n while condition() {\n update_condition();\n if x {\n // ...\n } else {\n continue;\n }\n println!(\"Hello, world\");\n }\n ```\n\n Could be rewritten as\n\n ```rust\n # fn condition() -> bool { false }\n # fn update_condition() {}\n # let x = false;\n while condition() {\n update_condition();\n if x {\n // ...\n println!(\"Hello, world\");\n }\n }\n ```\n\n As another example, the following code\n\n ```rust\n # fn waiting() -> bool { false }\n loop {\n if waiting() {\n continue;\n } else {\n // Do something useful\n }\n # break;\n }\n ```\n Could be rewritten as\n\n ```rust\n # fn waiting() -> bool { false }\n loop {\n if waiting() {\n continue;\n }\n // Do something useful\n # break;\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "needless_pass_by_value", - "id_span": { - "path": "src/needless_pass_by_value.rs", - "line": 50 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for functions taking arguments by value, but not consuming them in its\n body.\n\n **Why is this bad?** Taking arguments by reference is more flexible and can\n sometimes avoid\n unnecessary allocations.\n\n **Known problems:**\n * This lint suggests taking an argument by reference,\n however sometimes it is better to let users decide the argument type\n (by using `Borrow` trait, for example), depending on how the function is used.\n\n **Example:**\n ```rust\n fn foo(v: Vec) {\n assert_eq!(v.len(), 42);\n }\n ```\n should be\n ```rust\n fn foo(v: &[i32]) {\n assert_eq!(v.len(), 42);\n }\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": null - } - }, - { - "id": "needless_question_mark", - "id_span": { - "path": "src/needless_question_mark.rs", - "line": 57 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Suggests alternatives for useless applications of `?` in terminating expressions\n\n **Why is this bad?** There's no reason to use `?` to short-circuit when execution of the body will end there anyway.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n struct TO {\n magic: Option,\n }\n\n fn f(to: TO) -> Option {\n Some(to.magic?)\n }\n\n struct TR {\n magic: Result,\n }\n\n fn g(tr: Result) -> Result {\n tr.and_then(|t| Ok(t.magic?))\n }\n\n ```\n Use instead:\n ```rust\n struct TO {\n magic: Option,\n }\n\n fn f(to: TO) -> Option {\n to.magic\n }\n\n struct TR {\n magic: Result,\n }\n\n fn g(tr: Result) -> Result {\n tr.and_then(|t| t.magic)\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "needless_update", - "id_span": { - "path": "src/needless_update.rs", - "line": 43 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for needlessly including a base struct on update when all fields are changed anyway.\n\n This lint is not applied to structs marked with\n [non_exhaustive](https://doc.rust-lang.org/reference/attributes/type_system.html).\n\n **Why is this bad?** This will cost resources (because the base has to be\n somewhere), and make the code less readable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct Point {\n # x: i32,\n # y: i32,\n # z: i32,\n # }\n # let zero_point = Point { x: 0, y: 0, z: 0 };\n\n // Bad\n Point {\n x: 1,\n y: 1,\n z: 1,\n ..zero_point\n };\n\n // Ok\n Point {\n x: 1,\n y: 1,\n ..zero_point\n };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "neg_cmp_op_on_partial_ord", - "id_span": { - "path": "src/neg_cmp_op_on_partial_ord.rs", - "line": 41 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for the usage of negated comparison operators on types which only implement\n `PartialOrd` (e.g., `f64`).\n\n **Why is this bad?**\n These operators make it easy to forget that the underlying types actually allow not only three\n potential Orderings (Less, Equal, Greater) but also a fourth one (Uncomparable). This is\n especially easy to miss if the operator based comparison result is negated.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n use std::cmp::Ordering;\n\n // Bad\n let a = 1.0;\n let b = f64::NAN;\n\n let _not_less_or_equal = !(a <= b);\n\n // Good\n let a = 1.0;\n let b = f64::NAN;\n\n let _not_less_or_equal = match a.partial_cmp(&b) {\n None | Some(Ordering::Greater) => true,\n _ => false,\n };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "neg_multiply", - "id_span": { - "path": "src/neg_multiply.rs", - "line": 21 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for multiplication by -1 as a form of negation.\n **Why is this bad?** It's more readable to just negate.\n\n **Known problems:** This only catches integers (for now).\n\n **Example:**\n ```ignore\n x * -1\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "new_without_default", - "id_span": { - "path": "src/new_without_default.rs", - "line": 48 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for types with a `fn new() -> Self` method and no implementation of\n [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html).\n\n **Why is this bad?** The user might expect to be able to use\n [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) as the\n type can be constructed without arguments.\n\n **Known problems:** Hopefully none.\n\n **Example:**\n\n ```ignore\n struct Foo(Bar);\n\n impl Foo {\n fn new() -> Self {\n Foo(Bar::new())\n }\n }\n ```\n\n To fix the lint, add a `Default` implementation that delegates to `new`:\n\n ```ignore\n struct Foo(Bar);\n\n impl Default for Foo {\n fn default() -> Self {\n Foo::new()\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "no_effect", - "id_span": { - "path": "src/no_effect.rs", - "line": 22 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for statements which have no effect.\n **Why is this bad?** Similar to dead code, these statements are actually\n executed. However, as they have no effect, all they do is make the code less\n readable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n 0;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unnecessary_operation", - "id_span": { - "path": "src/no_effect.rs", - "line": 40 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for expression statements that can be reduced to a sub-expression.\n\n **Why is this bad?** Expressions by themselves often have no side-effects.\n Having such expressions reduces readability.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n compute_array()[0];\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "declare_interior_mutable_const", - "id_span": { - "path": "src/non_copy_const.rs", - "line": 69 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for declaration of `const` items which is interior mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.).\n\n **Why is this bad?** Consts are copied everywhere they are referenced, i.e.,\n every time you refer to the const a fresh instance of the `Cell` or `Mutex`\n or `AtomicXxxx` will be created, which defeats the whole purpose of using\n these types in the first place.\n\n The `const` should better be replaced by a `static` item if a global\n variable is wanted, or replaced by a `const fn` if a constructor is wanted.\n\n **Known problems:** A \"non-constant\" const item is a legacy way to supply an\n initialized value to downstream `static` items (e.g., the\n `std::sync::ONCE_INIT` constant). In this case the use of `const` is legit,\n and this lint should be suppressed.\n\n Even though the lint avoids triggering on a constant whose type has enums that have variants\n with interior mutability, and its value uses non interior mutable variants (see\n [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) and\n [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) for examples);\n it complains about associated constants without default values only based on its types;\n which might not be preferable.\n There're other enums plus associated constants cases that the lint cannot handle.\n\n Types that have underlying or potential interior mutability trigger the lint whether\n the interior mutable field is used or not. See issues\n [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and\n\n **Example:**\n ```rust\n use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};\n\n // Bad.\n const CONST_ATOM: AtomicUsize = AtomicUsize::new(12);\n CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged\n assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct\n\n // Good.\n static STATIC_ATOM: AtomicUsize = AtomicUsize::new(15);\n STATIC_ATOM.store(9, SeqCst);\n assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance\n ```\n", - "applicability": null - }, - { - "id": "borrow_interior_mutable_const", - "id_span": { - "path": "src/non_copy_const.rs", - "line": 110 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks if `const` items which is interior mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly.\n\n **Why is this bad?** Consts are copied everywhere they are referenced, i.e.,\n every time you refer to the const a fresh instance of the `Cell` or `Mutex`\n or `AtomicXxxx` will be created, which defeats the whole purpose of using\n these types in the first place.\n\n The `const` value should be stored inside a `static` item.\n\n **Known problems:** When an enum has variants with interior mutability, use of its non\n interior mutable variants can generate false positives. See issue\n [#3962](https://github.com/rust-lang/rust-clippy/issues/3962)\n\n Types that have underlying or potential interior mutability trigger the lint whether\n the interior mutable field is used or not. See issues\n [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and\n [#3825](https://github.com/rust-lang/rust-clippy/issues/3825)\n\n **Example:**\n ```rust\n use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};\n const CONST_ATOM: AtomicUsize = AtomicUsize::new(12);\n\n // Bad.\n CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged\n assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct\n\n // Good.\n static STATIC_ATOM: AtomicUsize = CONST_ATOM;\n STATIC_ATOM.store(9, SeqCst);\n assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance\n ```\n", - "applicability": null - }, - { - "id": "similar_names", - "id_span": { - "path": "src/non_expressive_names.rs", - "line": 27 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for names that are very similar and thus confusing.\n **Why is this bad?** It's hard to distinguish between names that differ only\n by a single character.\n\n **Known problems:** None?\n\n **Example:**\n ```ignore\n let checked_exp = something;\n let checked_expr = something_else;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "many_single_char_names", - "id_span": { - "path": "src/non_expressive_names.rs", - "line": 45 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for too many variables whose name consists of a single character.\n\n **Why is this bad?** It's hard to memorize what a variable means without a\n descriptive name.\n\n **Known problems:** None?\n\n **Example:**\n ```ignore\n let (a, b, c, d, e, f, g) = (...);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "just_underscores_and_digits", - "id_span": { - "path": "src/non_expressive_names.rs", - "line": 65 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks if you have variables whose name consists of just underscores and digits.\n\n **Why is this bad?** It's hard to memorize what a variable means without a\n descriptive name.\n\n **Known problems:** None?\n\n **Example:**\n ```rust\n let _1 = 1;\n let ___1 = 1;\n let __1___2 = 11;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "nonsensical_open_options", - "id_span": { - "path": "src/open_options.rs", - "line": 23 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for duplicate open options as well as combinations that make no sense.\n\n **Why is this bad?** In the best case, the code will be harder to read than\n necessary. I don't know the worst case.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n use std::fs::OpenOptions;\n\n OpenOptions::new().read(true).truncate(true);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "option_env_unwrap", - "id_span": { - "path": "src/option_env_unwrap.rs", - "line": 28 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for usage of `option_env!(...).unwrap()` and suggests usage of the `env!` macro.\n\n **Why is this bad?** Unwrapping the result of `option_env!` will panic\n at run-time if the environment variable doesn't exist, whereas `env!`\n catches it at compile-time.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,no_run\n let _ = option_env!(\"HOME\").unwrap();\n ```\n\n Is better expressed as:\n\n ```rust,no_run\n let _ = env!(\"HOME\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "option_if_let_else", - "id_span": { - "path": "src/option_if_let_else.rs", - "line": 59 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Lints usage of `if let Some(v) = ... { y } else { x }` which is more\n idiomatically done with `Option::map_or` (if the else bit is a pure\n expression) or `Option::map_or_else` (if the else bit is an impure\n expression).\n\n **Why is this bad?**\n Using the dedicated functions of the Option type is clearer and\n more concise than an `if let` expression.\n\n **Known problems:**\n This lint uses a deliberately conservative metric for checking\n if the inside of either body contains breaks or continues which will\n cause it to not suggest a fix if either block contains a loop with\n continues or breaks contained within the loop.\n\n **Example:**\n\n ```rust\n # let optional: Option = Some(0);\n # fn do_complicated_function() -> u32 { 5 };\n let _ = if let Some(foo) = optional {\n foo\n } else {\n 5\n };\n let _ = if let Some(foo) = optional {\n foo\n } else {\n let y = do_complicated_function();\n y*y\n };\n ```\n\n should be\n\n ```rust\n # let optional: Option = Some(0);\n # fn do_complicated_function() -> u32 { 5 };\n let _ = optional.map_or(5, |foo| foo);\n let _ = optional.map_or_else(||{\n let y = do_complicated_function();\n y*y\n }, |foo| foo);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "overflow_check_conditional", - "id_span": { - "path": "src/overflow_check_conditional.rs", - "line": 21 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Detects classic underflow/overflow checks.\n **Why is this bad?** Most classic C underflow/overflow checks will fail in\n Rust. Users can use functions like `overflowing_*` and `wrapping_*` instead.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let a = 1;\n # let b = 2;\n a + b < a;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "panic_in_result_fn", - "id_span": { - "path": "src/panic_in_result_fn.rs", - "line": 29 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!`, `unreachable!` or assertions in a function of type result.\n **Why is this bad?** For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence panicking macros should be avoided.\n\n **Known problems:** Functions called from a function returning a `Result` may invoke a panicking macro. This is not checked.\n\n **Example:**\n\n ```rust\n fn result_with_panic() -> Result\n {\n panic!(\"error\");\n }\n ```\n Use instead:\n ```rust\n fn result_without_panic() -> Result {\n Err(String::from(\"error\"))\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "panic", - "id_span": { - "path": "src/panic_unimplemented.rs", - "line": 19 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of `panic!`.\n **Why is this bad?** `panic!` will stop the execution of the executable\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n panic!(\"even with a good reason\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unimplemented", - "id_span": { - "path": "src/panic_unimplemented.rs", - "line": 35 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of `unimplemented!`.\n **Why is this bad?** This macro should not be present in production code\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n unimplemented!();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "todo", - "id_span": { - "path": "src/panic_unimplemented.rs", - "line": 51 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of `todo!`.\n **Why is this bad?** This macro should not be present in production code\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n todo!();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unreachable", - "id_span": { - "path": "src/panic_unimplemented.rs", - "line": 67 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of `unreachable!`.\n **Why is this bad?** This macro can cause code to panic\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n unreachable!();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "partialeq_ne_impl", - "id_span": { - "path": "src/partialeq_ne_impl.rs", - "line": 27 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for manual re-implementations of `PartialEq::ne`.\n **Why is this bad?** `PartialEq::ne` is required to always return the\n negated result of `PartialEq::eq`, which is exactly what the default\n implementation does. Therefore, there should never be any need to\n re-implement it.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n struct Foo;\n\n impl PartialEq for Foo {\n fn eq(&self, other: &Foo) -> bool { true }\n fn ne(&self, other: &Foo) -> bool { !(self == other) }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "trivially_copy_pass_by_ref", - "id_span": { - "path": "src/pass_by_ref_or_value.rs", - "line": 52 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for functions taking arguments by reference, where the argument type is `Copy` and small enough to be more efficient to always\n pass by value.\n\n **Why is this bad?** In many calling conventions instances of structs will\n be passed through registers if they fit into two or less general purpose\n registers.\n\n **Known problems:** This lint is target register size dependent, it is\n limited to 32-bit to try and reduce portability problems between 32 and\n 64-bit, but if you are compiling for 8 or 16-bit targets then the limit\n will be different.\n\n The configuration option `trivial_copy_size_limit` can be set to override\n this limit for a project.\n\n This lint attempts to allow passing arguments by reference if a reference\n to that argument is returned. This is implemented by comparing the lifetime\n of the argument and return value for equality. However, this can cause\n false positives in cases involving multiple lifetimes that are bounded by\n each other.\n\n **Example:**\n\n ```rust\n // Bad\n fn foo(v: &u32) {}\n ```\n\n ```rust\n // Better\n fn foo(v: u32) {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "large_types_passed_by_value", - "id_span": { - "path": "src/pass_by_ref_or_value.rs", - "line": 84 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for functions taking arguments by value, where the argument type is `Copy` and large enough to be worth considering\n passing by reference. Does not trigger if the function is being exported,\n because that might induce API breakage, if the parameter is declared as mutable,\n or if the argument is a `self`.\n\n **Why is this bad?** Arguments passed by value might result in an unnecessary\n shallow copy, taking up more space in the stack and requiring a call to\n `memcpy`, which can be expensive.\n\n **Example:**\n\n ```rust\n #[derive(Clone, Copy)]\n struct TooLarge([u8; 2048]);\n\n // Bad\n fn foo(v: TooLarge) {}\n ```\n ```rust\n #[derive(Clone, Copy)]\n struct TooLarge([u8; 2048]);\n\n // Good\n fn foo(v: &TooLarge) {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "path_buf_push_overwrite", - "id_span": { - "path": "src/path_buf_push_overwrite.rs", - "line": 36 - }, - "group": "clippy::nursery", - "docs": " **What it does:*** Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push) calls on `PathBuf` that can cause overwrites.\n\n **Why is this bad?** Calling `push` with a root path at the start can overwrite the\n previous defined path.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n use std::path::PathBuf;\n\n let mut x = PathBuf::from(\"/foo\");\n x.push(\"/bar\");\n assert_eq!(x, PathBuf::from(\"/bar\"));\n ```\n Could be written:\n\n ```rust\n use std::path::PathBuf;\n\n let mut x = PathBuf::from(\"/foo\");\n x.push(\"bar\");\n assert_eq!(x, PathBuf::from(\"/foo/bar\"));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "pattern_type_mismatch", - "id_span": { - "path": "src/pattern_type_mismatch.rs", - "line": 79 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for patterns that aren't exact representations of the types they are applied to.\n\n To satisfy this lint, you will have to adjust either the expression that is matched\n against or the pattern itself, as well as the bindings that are introduced by the\n adjusted patterns. For matching you will have to either dereference the expression\n with the `*` operator, or amend the patterns to explicitly match against `&`\n or `&mut ` depending on the reference mutability. For the bindings you need\n to use the inverse. You can leave them as plain bindings if you wish for the value\n to be copied, but you must use `ref mut ` or `ref ` to construct\n a reference into the matched structure.\n\n If you are looking for a way to learn about ownership semantics in more detail, it\n is recommended to look at IDE options available to you to highlight types, lifetimes\n and reference semantics in your code. The available tooling would expose these things\n in a general way even outside of the various pattern matching mechanics. Of course\n this lint can still be used to highlight areas of interest and ensure a good understanding\n of ownership semantics.\n\n **Why is this bad?** It isn't bad in general. But in some contexts it can be desirable\n because it increases ownership hints in the code, and will guard against some changes\n in ownership.\n\n **Known problems:** None.\n\n **Example:**\n\n This example shows the basic adjustments necessary to satisfy the lint. Note how\n the matched expression is explicitly dereferenced with `*` and the `inner` variable\n is bound to a shared borrow via `ref inner`.\n\n ```rust,ignore\n // Bad\n let value = &Some(Box::new(23));\n match value {\n Some(inner) => println!(\"{}\", inner),\n None => println!(\"none\"),\n }\n\n // Good\n let value = &Some(Box::new(23));\n match *value {\n Some(ref inner) => println!(\"{}\", inner),\n None => println!(\"none\"),\n }\n ```\n\n The following example demonstrates one of the advantages of the more verbose style.\n Note how the second version uses `ref mut a` to explicitly declare `a` a shared mutable\n borrow, while `b` is simply taken by value. This ensures that the loop body cannot\n accidentally modify the wrong part of the structure.\n\n ```rust,ignore\n // Bad\n let mut values = vec![(2, 3), (3, 4)];\n for (a, b) in &mut values {\n *a += *b;\n }\n\n // Good\n let mut values = vec![(2, 3), (3, 4)];\n for &mut (ref mut a, b) in &mut values {\n *a += b;\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "precedence", - "id_span": { - "path": "src/precedence.rs", - "line": 44 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for operations where precedence may be unclear and suggests to add parentheses. Currently it catches the following:\n * mixed usage of arithmetic and bit shifting/combining operators without\n parentheses\n * a \"negative\" numeric literal (which is really a unary `-` followed by a\n numeric literal)\n followed by a method call\n\n **Why is this bad?** Not everyone knows the precedence of those operators by\n heart, so expressions like these may trip others trying to reason about the\n code.\n\n **Known problems:** None.\n\n **Example:**\n * `1 << 2 + 3` equals 32, while `(1 << 2) + 3` equals 7\n * `-1i32.abs()` equals -1, while `(-1i32).abs()` equals 1\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "ptr_arg", - "id_span": { - "path": "src/ptr.rs", - "line": 69 - }, - "group": "clippy::style", - "docs": " **What it does:** This lint checks for function arguments of type `&String` or `&Vec` unless the references are mutable. It will also suggest you\n replace `.clone()` calls with the appropriate `.to_owned()`/`to_string()`\n calls.\n\n **Why is this bad?** Requiring the argument to be of the specific size\n makes the function less useful for no benefit; slices in the form of `&[T]`\n or `&str` usually suffice and can be obtained from other types, too.\n\n **Known problems:** The lint does not follow data. So if you have an\n argument `x` and write `let y = x; y.clone()` the lint will not suggest\n changing that `.clone()` to `.to_owned()`.\n\n Other functions called from this function taking a `&String` or `&Vec`\n argument may also fail to compile if you change the argument. Applying\n this lint on them will fix the problem, but they may be in other crates.\n\n One notable example of a function that may cause issues, and which cannot\n easily be changed due to being in the standard library is `Vec::contains`.\n when called on a `Vec>`. If a `&Vec` is passed to that method then\n it will compile, but if a `&[T]` is passed then it will not compile.\n\n ```ignore\n fn cannot_take_a_slice(v: &Vec) -> bool {\n let vec_of_vecs: Vec> = some_other_fn();\n\n vec_of_vecs.contains(v)\n }\n ```\n\n Also there may be `fn(&Vec)`-typed references pointing to your function.\n If you have them, you will get a compiler error after applying this lint's\n suggestions. You then have the choice to undo your changes or change the\n type of the reference.\n\n Note that if the function is part of your public interface, there may be\n other crates referencing it, of which you may not be aware. Carefully\n deprecate the function before applying the lint suggestions in this case.\n\n **Example:**\n ```ignore\n // Bad\n fn foo(&Vec) { .. }\n\n // Good\n fn foo(&[u32]) { .. }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "cmp_null", - "id_span": { - "path": "src/ptr.rs", - "line": 95 - }, - "group": "clippy::style", - "docs": " **What it does:** This lint checks for equality comparisons with `ptr::null`\n **Why is this bad?** It's easier and more readable to use the inherent\n `.is_null()`\n method instead\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n // Bad\n if x == ptr::null {\n ..\n }\n\n // Good\n if x.is_null() {\n ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mut_from_ref", - "id_span": { - "path": "src/ptr.rs", - "line": 117 - }, - "group": "clippy::correctness", - "docs": " **What it does:** This lint checks for functions that take immutable references and return mutable ones.\n\n **Why is this bad?** This is trivially unsound, as one can create two\n mutable references from the same (immutable!) source.\n This [error](https://github.com/rust-lang/rust/issues/39465)\n actually lead to an interim Rust release 1.15.1.\n\n **Known problems:** To be on the conservative side, if there's at least one\n mutable reference with the output lifetime, this lint will not trigger.\n In practice, this case is unlikely anyway.\n\n **Example:**\n ```ignore\n fn foo(&Foo) -> &mut Bar { .. }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "ptr_eq", - "id_span": { - "path": "src/ptr_eq.rs", - "line": 32 - }, - "group": "clippy::style", - "docs": " **What it does:** Use `std::ptr::eq` when applicable\n **Why is this bad?** `ptr::eq` can be used to compare `&T` references\n (which coerce to `*const T` implicitly) by their address rather than\n comparing the values they point to.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let a = &[1, 2, 3];\n let b = &[1, 2, 3];\n\n assert!(a as *const _ as usize == b as *const _ as usize);\n ```\n Use instead:\n ```rust\n let a = &[1, 2, 3];\n let b = &[1, 2, 3];\n\n assert!(std::ptr::eq(a, b));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "ptr_offset_with_cast", - "id_span": { - "path": "src/ptr_offset_with_cast.rs", - "line": 40 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of the `offset` pointer method with a `usize` casted to an `isize`.\n\n **Why is this bad?** If we’re always increasing the pointer address, we can avoid the numeric\n cast by using the `add` method instead.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n let vec = vec![b'a', b'b', b'c'];\n let ptr = vec.as_ptr();\n let offset = 1_usize;\n\n unsafe {\n ptr.offset(offset as isize);\n }\n ```\n\n Could be written:\n\n ```rust\n let vec = vec![b'a', b'b', b'c'];\n let ptr = vec.as_ptr();\n let offset = 1_usize;\n\n unsafe {\n ptr.add(offset);\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "question_mark", - "id_span": { - "path": "src/question_mark.rs", - "line": 34 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for expressions that could be replaced by the question mark operator.\n **Why is this bad?** Question mark usage is more idiomatic.\n\n **Known problems:** None\n\n **Example:**\n ```ignore\n if option.is_none() {\n return None;\n }\n ```\n\n Could be written:\n\n ```ignore\n option?;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "range_zip_with_len", - "id_span": { - "path": "src/ranges.rs", - "line": 40 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for zipping a collection with the range of `0.._.len()`.\n\n **Why is this bad?** The code is better expressed with `.enumerate()`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = vec![1];\n x.iter().zip(0..x.len());\n ```\n Could be written as\n ```rust\n # let x = vec![1];\n x.iter().enumerate();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "range_plus_one", - "id_span": { - "path": "src/ranges.rs", - "line": 74 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for exclusive ranges where 1 is added to the upper bound, e.g., `x..(y+1)`.\n\n **Why is this bad?** The code is more readable with an inclusive range\n like `x..=y`.\n\n **Known problems:** Will add unnecessary pair of parentheses when the\n expression is not wrapped in a pair but starts with a opening parenthesis\n and ends with a closing one.\n I.e., `let _ = (f()+1)..(f()+1)` results in `let _ = ((f()+1)..=f())`.\n\n Also in many cases, inclusive ranges are still slower to run than\n exclusive ranges, because they essentially add an extra branch that\n LLVM may fail to hoist out of the loop.\n\n This will cause a warning that cannot be fixed if the consumer of the\n range only accepts a specific range type, instead of the generic\n `RangeBounds` trait\n ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).\n\n **Example:**\n ```rust,ignore\n for x..(y+1) { .. }\n ```\n Could be written as\n ```rust,ignore\n for x..=y { .. }\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MachineApplicable" - } - }, - { - "id": "range_minus_one", - "id_span": { - "path": "src/ranges.rs", - "line": 99 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for inclusive ranges where 1 is subtracted from the upper bound, e.g., `x..=(y-1)`.\n\n **Why is this bad?** The code is more readable with an exclusive range\n like `x..y`.\n\n **Known problems:** This will cause a warning that cannot be fixed if\n the consumer of the range only accepts a specific range type, instead of\n the generic `RangeBounds` trait\n ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).\n\n **Example:**\n ```rust,ignore\n for x..=(y-1) { .. }\n ```\n Could be written as\n ```rust,ignore\n for x..y { .. }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "reversed_empty_ranges", - "id_span": { - "path": "src/ranges.rs", - "line": 132 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for range expressions `x..y` where both `x` and `y` are constant and `x` is greater or equal to `y`.\n\n **Why is this bad?** Empty ranges yield no values so iterating them is a no-op.\n Moreover, trying to use a reversed range to index a slice will panic at run-time.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,no_run\n fn main() {\n (10..=0).for_each(|x| println!(\"{}\", x));\n\n let arr = [1, 2, 3, 4, 5];\n let sub = &arr[3..1];\n }\n ```\n Use instead:\n ```rust\n fn main() {\n (0..=10).rev().for_each(|x| println!(\"{}\", x));\n\n let arr = [1, 2, 3, 4, 5];\n let sub = &arr[1..3];\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "manual_range_contains", - "id_span": { - "path": "src/ranges.rs", - "line": 159 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for expressions like `x >= 3 && x < 8` that could be more readably expressed as `(3..8).contains(x)`.\n\n **Why is this bad?** `contains` expresses the intent better and has less\n failure modes (such as fencepost errors or using `||` instead of `&&`).\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // given\n let x = 6;\n\n assert!(x >= 3 && x < 8);\n ```\n Use instead:\n ```rust\n# let x = 6;\n assert!((3..8).contains(&x));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "redundant_clone", - "id_span": { - "path": "src/redundant_clone.rs", - "line": 63 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for a redundant `clone()` (and its relatives) which clones an owned value that is going to be dropped without further use.\n\n **Why is this bad?** It is not always possible for the compiler to eliminate useless\n allocations and deallocations generated by redundant `clone()`s.\n\n **Known problems:**\n\n False-negatives: analysis performed by this lint is conservative and limited.\n\n **Example:**\n ```rust\n # use std::path::Path;\n # #[derive(Clone)]\n # struct Foo;\n # impl Foo {\n # fn new() -> Self { Foo {} }\n # }\n # fn call(x: Foo) {}\n {\n let x = Foo::new();\n call(x.clone());\n call(x.clone()); // this can just pass `x`\n }\n\n [\"lorem\", \"ipsum\"].join(\" \").to_string();\n\n Path::new(\"/a/b\").join(\"c\").to_path_buf();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "redundant_closure_call", - "id_span": { - "path": "src/redundant_closure_call.rs", - "line": 32 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Detects closures called in the same expression where they are defined.\n\n **Why is this bad?** It is unnecessarily adding to the expression's\n complexity.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n // Bad\n let a = (|| 42)()\n\n // Good\n let a = 42\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "redundant_else", - "id_span": { - "path": "src/redundant_else.rs", - "line": 37 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for `else` blocks that can be removed without changing semantics.\n **Why is this bad?** The `else` block adds unnecessary indentation and verbosity.\n\n **Known problems:** Some may prefer to keep the `else` block for clarity.\n\n **Example:**\n\n ```rust\n fn my_func(count: u32) {\n if count == 0 {\n print!(\"Nothing to do\");\n return;\n } else {\n print!(\"Moving on...\");\n }\n }\n ```\n Use instead:\n ```rust\n fn my_func(count: u32) {\n if count == 0 {\n print!(\"Nothing to do\");\n return;\n }\n print!(\"Moving on...\");\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "redundant_field_names", - "id_span": { - "path": "src/redundant_field_names.rs", - "line": 34 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for fields in struct literals where shorthands could be used.\n\n **Why is this bad?** If the field and variable names are the same,\n the field name is redundant.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let bar: u8 = 123;\n\n struct Foo {\n bar: u8,\n }\n\n let foo = Foo { bar: bar };\n ```\n the last line can be simplified to\n ```ignore\n let foo = Foo { bar };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "redundant_pub_crate", - "id_span": { - "path": "src/redundant_pub_crate.rs", - "line": 30 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for items declared `pub(crate)` that are not crate visible because they are inside a private module.\n\n **Why is this bad?** Writing `pub(crate)` is misleading when it's redundant due to the parent\n module's visibility.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n mod internal {\n pub(crate) fn internal_fn() { }\n }\n ```\n This function is not visible outside the module and it can be declared with `pub` or\n private visibility\n ```rust\n mod internal {\n pub fn internal_fn() { }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "redundant_slicing", - "id_span": { - "path": "src/redundant_slicing.rs", - "line": 33 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for redundant slicing expressions which use the full range, and do not change the type.\n\n **Why is this bad?** It unnecessarily adds complexity to the expression.\n\n **Known problems:** If the type being sliced has an implementation of `Index`\n that actually changes anything then it can't be removed. However, this would be surprising\n to people reading the code and should have a note with it.\n\n **Example:**\n\n ```ignore\n fn get_slice(x: &[u32]) -> &[u32] {\n &x[..]\n }\n ```\n Use instead:\n ```ignore\n fn get_slice(x: &[u32]) -> &[u32] {\n x\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "redundant_static_lifetimes", - "id_span": { - "path": "src/redundant_static_lifetimes.rs", - "line": 30 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for constants and statics with an explicit `'static` lifetime.\n **Why is this bad?** Adding `'static` to every reference can create very\n complicated types.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n const FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] =\n &[...]\n static FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] =\n &[...]\n ```\n This code can be rewritten as\n ```ignore\n const FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]\n static FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "ref_option_ref", - "id_span": { - "path": "src/ref_option_ref.rs", - "line": 28 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for usage of `&Option<&T>`.\n **Why is this bad?** Since `&` is Copy, it's useless to have a\n reference on `Option<&T>`.\n\n **Known problems:** It may be irrelevant to use this lint on\n public API code as it will make a breaking change to apply it.\n\n **Example:**\n\n ```rust,ignore\n let x: &Option<&u32> = &Some(&0u32);\n ```\n Use instead:\n ```rust,ignore\n let x: Option<&u32> = Some(&0u32);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "deref_addrof", - "id_span": { - "path": "src/reference.rs", - "line": 29 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `*&` and `*&mut` in expressions.\n **Why is this bad?** Immediately dereferencing a reference is no-op and\n makes the code less clear.\n\n **Known problems:** Multiple dereference/addrof pairs are not handled so\n the suggested fix for `x = **&&y` is `x = *&y`, which is still incorrect.\n\n **Example:**\n ```rust,ignore\n // Bad\n let a = f(*&mut b);\n let c = *&d;\n\n // Good\n let a = f(b);\n let c = d;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "ref_in_deref", - "id_span": { - "path": "src/reference.rs", - "line": 120 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for references in expressions that use auto dereference.\n\n **Why is this bad?** The reference is a no-op and is automatically\n dereferenced by the compiler and makes the code less clear.\n\n **Example:**\n ```rust\n struct Point(u32, u32);\n let point = Point(30, 20);\n let x = (&point).0;\n ```\n Use instead:\n ```rust\n # struct Point(u32, u32);\n # let point = Point(30, 20);\n let x = point.0;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "invalid_regex", - "id_span": { - "path": "src/regex.rs", - "line": 25 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks [regex](https://crates.io/crates/regex) creation (with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`) for correct\n regex syntax.\n\n **Why is this bad?** This will lead to a runtime panic.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n Regex::new(\"|\")\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "trivial_regex", - "id_span": { - "path": "src/regex.rs", - "line": 46 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for trivial [regex](https://crates.io/crates/regex) creation (with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`).\n\n **Why is this bad?** Matching the regex can likely be replaced by `==` or\n `str::starts_with`, `str::ends_with` or `std::contains` or other `str`\n methods.\n\n **Known problems:** If the same regex is going to be applied to multiple\n inputs, the precomputations done by `Regex` construction can give\n significantly better performance than any of the `str`-based methods.\n\n **Example:**\n ```ignore\n Regex::new(\"^foobar\")\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "repeat_once", - "id_span": { - "path": "src/repeat_once.rs", - "line": 33 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `.repeat(1)` and suggest the following method for each types. - `.to_string()` for `str`\n - `.clone()` for `String`\n - `.to_vec()` for `slice`\n\n **Why is this bad?** For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning the string is the intention behind this, `clone()` should be used.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n fn main() {\n let x = String::from(\"hello world\").repeat(1);\n }\n ```\n Use instead:\n ```rust\n fn main() {\n let x = String::from(\"hello world\").clone();\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "let_and_return", - "id_span": { - "path": "src/returns.rs", - "line": 38 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `let`-bindings, which are subsequently returned.\n\n **Why is this bad?** It is just extraneous code. Remove it to make your code\n more rusty.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn foo() -> String {\n let x = String::new();\n x\n }\n ```\n instead, use\n ```\n fn foo() -> String {\n String::new()\n }\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MachineApplicable" - } - }, - { - "id": "needless_return", - "id_span": { - "path": "src/returns.rs", - "line": 63 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for return statements at the end of a block.\n **Why is this bad?** Removing the `return` and semicolon will make the code\n more rusty.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn foo(x: usize) -> usize {\n return x;\n }\n ```\n simplify to\n ```rust\n fn foo(x: usize) -> usize {\n x\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "self_assignment", - "id_span": { - "path": "src/self_assignment.rs", - "line": 29 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for explicit self-assignments.\n **Why is this bad?** Self-assignments are redundant and unlikely to be\n intentional.\n\n **Known problems:** If expression contains any deref coercions or\n indexing operations they are assumed not to have any side effects.\n\n **Example:**\n\n ```rust\n struct Event {\n id: usize,\n x: i32,\n y: i32,\n }\n\n fn copy_position(a: &mut Event, b: &Event) {\n a.x = b.x;\n a.y = a.y;\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "semicolon_if_nothing_returned", - "id_span": { - "path": "src/semicolon_if_nothing_returned.rs", - "line": 30 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Looks for blocks of expressions and fires if the last expression returns `()` but is not followed by a semicolon.\n\n **Why is this bad?** The semicolon might be optional but when\n extending the block with new code, it doesn't require a change in previous last line.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n fn main() {\n println!(\"Hello world\")\n }\n ```\n Use instead:\n ```rust\n fn main() {\n println!(\"Hello world\");\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "serde_api_misuse", - "id_span": { - "path": "src/serde_api.rs", - "line": 16 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for mis-uses of the serde API.\n **Why is this bad?** Serde is very finnicky about how its API should be\n used, but the type system can't be used to enforce it (yet?).\n\n **Known problems:** None.\n\n **Example:** Implementing `Visitor::visit_string` but not\n `Visitor::visit_str`.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "shadow_same", - "id_span": { - "path": "src/shadow.rs", - "line": 34 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for bindings that shadow other bindings already in scope, while just changing reference level or mutability.\n\n **Why is this bad?** Not much, in fact it's a very common pattern in Rust\n code. Still, some may opt to avoid it in their code base, they can set this\n lint to `Warn`.\n\n **Known problems:** This lint, as the other shadowing related lints,\n currently only catches very simple patterns.\n\n **Example:**\n ```rust\n # let x = 1;\n // Bad\n let x = &x;\n\n // Good\n let y = &x; // use different variable name\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "shadow_reuse", - "id_span": { - "path": "src/shadow.rs", - "line": 61 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for bindings that shadow other bindings already in scope, while reusing the original value.\n\n **Why is this bad?** Not too much, in fact it's a common pattern in Rust\n code. Still, some argue that name shadowing like this hurts readability,\n because a value may be bound to different things depending on position in\n the code.\n\n **Known problems:** This lint, as the other shadowing related lints,\n currently only catches very simple patterns.\n\n **Example:**\n ```rust\n let x = 2;\n let x = x + 1;\n ```\n use different variable name:\n ```rust\n let x = 2;\n let y = x + 1;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "shadow_unrelated", - "id_span": { - "path": "src/shadow.rs", - "line": 93 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for bindings that shadow other bindings already in scope, either without a initialization or with one that does not even use\n the original value.\n\n **Why is this bad?** Name shadowing can hurt readability, especially in\n large code bases, because it is easy to lose track of the active binding at\n any place in the code. This can be alleviated by either giving more specific\n names to bindings or introducing more scopes to contain the bindings.\n\n **Known problems:** This lint, as the other shadowing related lints,\n currently only catches very simple patterns. Note that\n `allow`/`warn`/`deny`/`forbid` attributes only work on the function level\n for this lint.\n\n **Example:**\n ```rust\n # let y = 1;\n # let z = 2;\n let x = y;\n\n // Bad\n let x = z; // shadows the earlier binding\n\n // Good\n let w = z; // use different variable name\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "single_component_path_imports", - "id_span": { - "path": "src/single_component_path_imports.rs", - "line": 32 - }, - "group": "clippy::style", - "docs": " **What it does:** Checking for imports with single component use path.\n **Why is this bad?** Import with single component use path such as `use cratename;`\n is not necessary, and thus should be removed.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,ignore\n use regex;\n\n fn main() {\n regex::Regex::new(r\"^\\d{4}-\\d{2}-\\d{2}$\").unwrap();\n }\n ```\n Better as\n ```rust,ignore\n fn main() {\n regex::Regex::new(r\"^\\d{4}-\\d{2}-\\d{2}$\").unwrap();\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "size_of_in_element_count", - "id_span": { - "path": "src/size_of_in_element_count.rs", - "line": 31 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Detects expressions where `size_of::` or `size_of_val::` is used as a\n count of elements of type `T`\n\n **Why is this bad?** These functions expect a count\n of `T` and not a number of bytes\n\n **Known problems:** None.\n\n **Example:**\n ```rust,no_run\n # use std::ptr::copy_nonoverlapping;\n # use std::mem::size_of;\n const SIZE: usize = 128;\n let x = [2u8; SIZE];\n let mut y = [2u8; SIZE];\n unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "slow_vector_initialization", - "id_span": { - "path": "src/slow_vector_initialization.rs", - "line": 37 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks slow zero-filled vector initialization\n **Why is this bad?** These structures are non-idiomatic and less efficient than simply using\n `vec![0; len]`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use core::iter::repeat;\n # let len = 4;\n\n // Bad\n let mut vec1 = Vec::with_capacity(len);\n vec1.resize(len, 0);\n\n let mut vec2 = Vec::with_capacity(len);\n vec2.extend(repeat(0).take(len));\n\n // Good\n let mut vec1 = vec![0; len];\n let mut vec2 = vec![0; len];\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "stable_sort_primitive", - "id_span": { - "path": "src/stable_sort_primitive.rs", - "line": 36 - }, - "group": "clippy::perf", - "docs": " **What it does:** When sorting primitive values (integers, bools, chars, as well\n as arrays, slices, and tuples of such items), it is better to\n use an unstable sort than a stable sort.\n\n **Why is this bad?**\n Using a stable sort consumes more memory and cpu cycles. Because\n values which compare equal are identical, preserving their\n relative order (the guarantee that a stable sort provides) means\n nothing, while the extra costs still apply.\n\n **Known problems:**\n None\n\n **Example:**\n\n ```rust\n let mut vec = vec![2, 1, 3];\n vec.sort();\n ```\n Use instead:\n ```rust\n let mut vec = vec![2, 1, 3];\n vec.sort_unstable();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "string_add_assign", - "id_span": { - "path": "src/strings.rs", - "line": 37 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for string appends of the form `x = x + y` (without `let`!).\n\n **Why is this bad?** It's not really bad, but some people think that the\n `.push_str(_)` method is more readable.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let mut x = \"Hello\".to_owned();\n x = x + \", World\";\n\n // More readable\n x += \", World\";\n x.push_str(\", World\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "string_add", - "id_span": { - "path": "src/strings.rs", - "line": 65 - }, - "group": "clippy::restriction", - "docs": " **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*\n match.\n\n **Why is this bad?** It's not bad in and of itself. However, this particular\n `Add` implementation is asymmetric (the other operand need not be `String`,\n but `x` does), while addition as mathematically defined is symmetric, also\n the `String::push_str(_)` function is a perfectly good replacement.\n Therefore, some dislike it and wish not to have it in their code.\n\n That said, other people think that string addition, having a long tradition\n in other languages is actually fine, which is why we decided to make this\n particular lint `allow` by default.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let x = \"Hello\".to_owned();\n x + \", World\";\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "string_lit_as_bytes", - "id_span": { - "path": "src/strings.rs", - "line": 107 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for the `as_bytes` method called on string literals that contain only ASCII characters.\n\n **Why is this bad?** Byte string literals (e.g., `b\"foo\"`) can be used\n instead. They are shorter but less discoverable than `as_bytes()`.\n\n **Known Problems:**\n `\"str\".as_bytes()` and the suggested replacement of `b\"str\"` are not\n equivalent because they have different types. The former is `&[u8]`\n while the latter is `&[u8; 3]`. That means in general they will have a\n different set of methods and different trait implementations.\n\n ```compile_fail\n fn f(v: Vec) {}\n\n f(\"...\".as_bytes().to_owned()); // works\n f(b\"...\".to_owned()); // does not work, because arg is [u8; 3] not Vec\n\n fn g(r: impl std::io::Read) {}\n\n g(\"...\".as_bytes()); // works\n g(b\"...\"); // does not work\n ```\n\n The actual equivalent of `\"str\".as_bytes()` with the same type is not\n `b\"str\"` but `&b\"str\"[..]`, which is a great deal of punctuation and not\n more readable than a function call.\n\n **Example:**\n ```rust\n // Bad\n let bs = \"a byte string\".as_bytes();\n\n // Good\n let bs = b\"a byte string\";\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "string_from_utf8_as_bytes", - "id_span": { - "path": "src/strings.rs", - "line": 196 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Check if the string is transformed to byte array and casted back to string.\n **Why is this bad?** It's unnecessary, the string can be used directly.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n let _ = std::str::from_utf8(&\"Hello World!\".as_bytes()[6..11]).unwrap();\n ```\n could be written as\n ```rust\n let _ = &\"Hello World!\"[6..11];\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "str_to_string", - "id_span": { - "path": "src/strings.rs", - "line": 313 - }, - "group": "clippy::restriction", - "docs": " **What it does:** This lint checks for `.to_string()` method calls on values of type `&str`.\n **Why is this bad?** The `to_string` method is also used on other types to convert them to a string.\n When called on a `&str` it turns the `&str` into the owned variant `String`, which can be better\n expressed with `.to_owned()`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // example code where clippy issues a warning\n let _ = \"str\".to_string();\n ```\n Use instead:\n ```rust\n // example code which does not raise clippy warning\n let _ = \"str\".to_owned();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "string_to_string", - "id_span": { - "path": "src/strings.rs", - "line": 362 - }, - "group": "clippy::restriction", - "docs": " **What it does:** This lint checks for `.to_string()` method calls on values of type `String`.\n **Why is this bad?** The `to_string` method is also used on other types to convert them to a string.\n When called on a `String` it only clones the `String`, which can be better expressed with `.clone()`.\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // example code where clippy issues a warning\n let msg = String::from(\"Hello World\");\n let _ = msg.to_string();\n ```\n Use instead:\n ```rust\n // example code which does not raise clippy warning\n let msg = String::from(\"Hello World\");\n let _ = msg.clone();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "suspicious_operation_groupings", - "id_span": { - "path": "src/suspicious_operation_groupings.rs", - "line": 60 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for unlikely usages of binary operators that are almost\n certainly typos and/or copy/paste errors, given the other usages\n of binary operators nearby.\n **Why is this bad?**\n They are probably bugs and if they aren't then they look like bugs\n and you should add a comment explaining why you are doing such an\n odd set of operations.\n **Known problems:**\n There may be some false positives if you are trying to do something\n unusual that happens to look like a typo.\n\n **Example:**\n\n ```rust\n struct Vec3 {\n x: f64,\n y: f64,\n z: f64,\n }\n\n impl Eq for Vec3 {}\n\n impl PartialEq for Vec3 {\n fn eq(&self, other: &Self) -> bool {\n // This should trigger the lint because `self.x` is compared to `other.y`\n self.x == other.y && self.y == other.y && self.z == other.z\n }\n }\n ```\n Use instead:\n ```rust\n # struct Vec3 {\n # x: f64,\n # y: f64,\n # z: f64,\n # }\n // same as above except:\n impl PartialEq for Vec3 {\n fn eq(&self, other: &Self) -> bool {\n // Note we now compare other.x to self.x\n self.x == other.x && self.y == other.y && self.z == other.z\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "suspicious_arithmetic_impl", - "id_span": { - "path": "src/suspicious_trait_impl.rs", - "line": 27 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Lints for suspicious operations in impls of arithmetic operators, e.g. subtracting elements in an Add impl.\n\n **Why this is bad?** This is probably a typo or copy-and-paste error and not intended.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n impl Add for Foo {\n type Output = Foo;\n\n fn add(self, other: Foo) -> Foo {\n Foo(self.0 - other.0)\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "suspicious_op_assign_impl", - "id_span": { - "path": "src/suspicious_trait_impl.rs", - "line": 48 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Lints for suspicious operations in impls of OpAssign, e.g. subtracting elements in an AddAssign impl.\n\n **Why this is bad?** This is probably a typo or copy-and-paste error and not intended.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n impl AddAssign for Foo {\n fn add_assign(&mut self, other: Foo) {\n *self = *self - other;\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "manual_swap", - "id_span": { - "path": "src/swap.rs", - "line": 36 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for manual swapping.\n **Why is this bad?** The `std::mem::swap` function exposes the intent better\n without deinitializing or copying either variable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let mut a = 42;\n let mut b = 1337;\n\n let t = b;\n b = a;\n a = t;\n ```\n Use std::mem::swap():\n ```rust\n let mut a = 1;\n let mut b = 2;\n std::mem::swap(&mut a, &mut b);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "almost_swapped", - "id_span": { - "path": "src/swap.rs", - "line": 61 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `foo = bar; bar = foo` sequences.\n **Why is this bad?** This looks like a failed attempt to swap.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let mut a = 1;\n # let mut b = 2;\n a = b;\n b = a;\n ```\n If swapping is intended, use `swap()` instead:\n ```rust\n # let mut a = 1;\n # let mut b = 2;\n std::mem::swap(&mut a, &mut b);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "tabs_in_doc_comments", - "id_span": { - "path": "src/tabs_in_doc_comments.rs", - "line": 54 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks doc comments for usage of tab characters.\n **Why is this bad?** The rust style-guide promotes spaces instead of tabs for indentation.\n To keep a consistent view on the source, also doc comments should not have tabs.\n Also, explaining ascii-diagrams containing tabs can get displayed incorrectly when the\n display settings of the author and reader differ.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n ///\n /// Struct to hold two strings:\n /// \t- first\t\tone\n /// \t- second\tone\n pub struct DoubleString {\n ///\n /// \t- First String:\n /// \t\t- needs to be inside here\n first_string: String,\n ///\n /// \t- Second String:\n /// \t\t- needs to be inside here\n second_string: String,\n}\n ```\n\n Will be converted to:\n ```rust\n ///\n /// Struct to hold two strings:\n /// - first one\n /// - second one\n pub struct DoubleString {\n ///\n /// - First String:\n /// - needs to be inside here\n first_string: String,\n ///\n /// - Second String:\n /// - needs to be inside here\n second_string: String,\n}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "temporary_assignment", - "id_span": { - "path": "src/temporary_assignment.rs", - "line": 19 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for construction of a structure or tuple just to assign a value in it.\n\n **Why is this bad?** Readability. If the structure is only created to be\n updated, why not write the structure you want in the first place?\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n (0, 0).0 = 1\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "to_digit_is_some", - "id_span": { - "path": "src/to_digit_is_some.rs", - "line": 27 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `.to_digit(..).is_some()` on `char`s.\n **Why is this bad?** This is a convoluted way of checking if a `char` is a digit. It's\n more straight forward to use the dedicated `is_digit` method.\n\n **Example:**\n ```rust\n # let c = 'c';\n # let radix = 10;\n let is_digit = c.to_digit(radix).is_some();\n ```\n can be written as:\n ```\n # let c = 'c';\n # let radix = 10;\n let is_digit = c.is_digit(radix);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "to_string_in_display", - "id_span": { - "path": "src/to_string_in_display.rs", - "line": 40 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for uses of `to_string()` in `Display` traits.\n **Why is this bad?** Usually `to_string` is implemented indirectly\n via `Display`. Hence using it while implementing `Display` would\n lead to infinite recursion.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n use std::fmt;\n\n struct Structure(i32);\n impl fmt::Display for Structure {\n fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n write!(f, \"{}\", self.to_string())\n }\n }\n\n ```\n Use instead:\n ```rust\n use std::fmt;\n\n struct Structure(i32);\n impl fmt::Display for Structure {\n fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n write!(f, \"{}\", self.0)\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "type_repetition_in_bounds", - "id_span": { - "path": "src/trait_bounds.rs", - "line": 28 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** This lint warns about unnecessary type repetitions in trait bounds\n **Why is this bad?** Repeating the type for every bound makes the code\n less readable than combining the bounds\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n pub fn foo(t: T) where T: Copy, T: Clone {}\n ```\n\n Could be written as:\n\n ```rust\n pub fn foo(t: T) where T: Copy + Clone {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "trait_duplication_in_bounds", - "id_span": { - "path": "src/trait_bounds.rs", - "line": 57 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for cases where generics are being used and multiple syntax specifications for trait bounds are used simultaneously.\n\n **Why is this bad?** Duplicate bounds makes the code\n less readable than specifing them only once.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn func(arg: T) where T: Clone + Default {}\n ```\n\n Could be written as:\n\n ```rust\n fn func(arg: T) {}\n ```\n or\n\n ```rust\n fn func(arg: T) where T: Clone + Default {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "wrong_transmute", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 34 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for transmutes that can't ever be correct on any architecture.\n\n **Why is this bad?** It's basically guaranteed to be undefined behaviour.\n\n **Known problems:** When accessing C, users might want to store pointer\n sized objects in `extradata` arguments to save an allocation.\n\n **Example:**\n ```ignore\n let ptr: *const T = core::intrinsics::transmute('x')\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "useless_transmute", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 53 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for transmutes to the original type of the object and transmutes that could be a cast.\n\n **Why is this bad?** Readability. The code tricks people into thinking that\n something complex is going on.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n core::intrinsics::transmute(t); // where the result type is the same as `t`'s\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "transmutes_expressible_as_ptr_casts", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 78 - }, - "group": "clippy::complexity", - "docs": " **What it does:**Checks for transmutes that could be a pointer cast.\n **Why is this bad?** Readability. The code tricks people into thinking that\n something complex is going on.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # let p: *const [i32] = &[];\n unsafe { std::mem::transmute::<*const [i32], *const [u16]>(p) };\n ```\n Use instead:\n ```rust\n # let p: *const [i32] = &[];\n p as *const [u16];\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "crosspointer_transmute", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 96 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for transmutes between a type `T` and `*T`.\n **Why is this bad?** It's easy to mistakenly transmute between a type and a\n pointer to that type.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n core::intrinsics::transmute(t) // where the result type is the same as\n // `*t` or `&t`'s\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "transmute_ptr_to_ref", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 121 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for transmutes from a pointer to a reference.\n **Why is this bad?** This can always be rewritten with `&` and `*`.\n\n **Known problems:**\n - `mem::transmute` in statics and constants is stable from Rust 1.46.0,\n while dereferencing raw pointer is not stable yet.\n If you need to do this in those places,\n you would have to use `transmute` instead.\n\n **Example:**\n ```rust,ignore\n unsafe {\n let _: &T = std::mem::transmute(p); // where p: *const T\n }\n\n // can be written:\n let _: &T = &*p;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "transmute_int_to_char", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 152 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for transmutes from an integer to a `char`.\n **Why is this bad?** Not every integer is a Unicode scalar value.\n\n **Known problems:**\n - [`from_u32`] which this lint suggests using is slower than `transmute`\n as it needs to validate the input.\n If you are certain that the input is always a valid Unicode scalar value,\n use [`from_u32_unchecked`] which is as fast as `transmute`\n but has a semantically meaningful name.\n - You might want to handle `None` returned from [`from_u32`] instead of calling `unwrap`.\n\n [`from_u32`]: https://doc.rust-lang.org/std/char/fn.from_u32.html\n [`from_u32_unchecked`]: https://doc.rust-lang.org/std/char/fn.from_u32_unchecked.html\n\n **Example:**\n ```rust\n let x = 1_u32;\n unsafe {\n let _: char = std::mem::transmute(x); // where x: u32\n }\n\n // should be:\n let _ = std::char::from_u32(x).unwrap();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "transmute_bytes_to_str", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 183 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for transmutes from a `&[u8]` to a `&str`.\n **Why is this bad?** Not every byte slice is a valid UTF-8 string.\n\n **Known problems:**\n - [`from_utf8`] which this lint suggests using is slower than `transmute`\n as it needs to validate the input.\n If you are certain that the input is always a valid UTF-8,\n use [`from_utf8_unchecked`] which is as fast as `transmute`\n but has a semantically meaningful name.\n - You might want to handle errors returned from [`from_utf8`] instead of calling `unwrap`.\n\n [`from_utf8`]: https://doc.rust-lang.org/std/str/fn.from_utf8.html\n [`from_utf8_unchecked`]: https://doc.rust-lang.org/std/str/fn.from_utf8_unchecked.html\n\n **Example:**\n ```rust\n let b: &[u8] = &[1_u8, 2_u8];\n unsafe {\n let _: &str = std::mem::transmute(b); // where b: &[u8]\n }\n\n // should be:\n let _ = std::str::from_utf8(b).unwrap();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "transmute_int_to_bool", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 205 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for transmutes from an integer to a `bool`.\n **Why is this bad?** This might result in an invalid in-memory representation of a `bool`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = 1_u8;\n unsafe {\n let _: bool = std::mem::transmute(x); // where x: u8\n }\n\n // should be:\n let _: bool = x != 0;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "transmute_int_to_float", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 227 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for transmutes from an integer to a float.\n **Why is this bad?** Transmutes are dangerous and error-prone, whereas `from_bits` is intuitive\n and safe.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n unsafe {\n let _: f32 = std::mem::transmute(1_u32); // where x: u32\n }\n\n // should be:\n let _: f32 = f32::from_bits(1_u32);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "transmute_float_to_int", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 249 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for transmutes from a float to an integer.\n **Why is this bad?** Transmutes are dangerous and error-prone, whereas `to_bits` is intuitive\n and safe.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n unsafe {\n let _: u32 = std::mem::transmute(1f32);\n }\n\n // should be:\n let _: u32 = 1f32.to_bits();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "transmute_ptr_to_ptr", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 276 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for transmutes from a pointer to a pointer, or from a reference to a reference.\n\n **Why is this bad?** Transmutes are dangerous, and these can instead be\n written as casts.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let ptr = &1u32 as *const u32;\n unsafe {\n // pointer-to-pointer transmute\n let _: *const f32 = std::mem::transmute(ptr);\n // ref-ref transmute\n let _: &f32 = std::mem::transmute(&1u32);\n }\n // These can be respectively written:\n let _ = ptr as *const f32;\n let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "unsound_collection_transmute", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 304 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for transmutes between collections whose types have different ABI, size or alignment.\n\n **Why is this bad?** This is undefined behavior.\n\n **Known problems:** Currently, we cannot know whether a type is a\n collection, so we just lint the ones that come with `std`.\n\n **Example:**\n ```rust\n // different size, therefore likely out-of-bounds memory access\n // You absolutely do not want this in your code!\n unsafe {\n std::mem::transmute::<_, Vec>(vec![2_u16])\n };\n ```\n\n You must always iterate, map and collect the values:\n\n ```rust\n vec![2_u16].into_iter().map(u32::from).collect::>();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "transmuting_null", - "id_span": { - "path": "src/transmuting_null.rs", - "line": 23 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for transmute calls which would receive a null pointer.\n **Why is this bad?** Transmuting a null pointer is undefined behavior.\n\n **Known problems:** Not all cases can be detected at the moment of this writing.\n For example, variables which hold a null pointer and are then fed to a `transmute`\n call, aren't detectable yet.\n\n **Example:**\n ```rust\n let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "try_err", - "id_span": { - "path": "src/try_err.rs", - "line": 43 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usages of `Err(x)?`.\n **Why is this bad?** The `?` operator is designed to allow calls that\n can fail to be easily chained. For example, `foo()?.bar()` or\n `foo(bar()?)`. Because `Err(x)?` can't be used that way (it will\n always return), it is more clear to write `return Err(x)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn foo(fail: bool) -> Result {\n if fail {\n Err(\"failed\")?;\n }\n Ok(0)\n }\n ```\n Could be written:\n\n ```rust\n fn foo(fail: bool) -> Result {\n if fail {\n return Err(\"failed\".into());\n }\n Ok(0)\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "box_vec", - "id_span": { - "path": "src/types.rs", - "line": 66 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for use of `Box>` anywhere in the code. Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.\n\n **Why is this bad?** `Vec` already keeps its contents in a separate area on\n the heap. So if you `Box` it, you just add another level of indirection\n without any benefit whatsoever.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n struct X {\n values: Box>,\n }\n ```\n\n Better:\n\n ```rust,ignore\n struct X {\n values: Vec,\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "vec_box", - "id_span": { - "path": "src/types.rs", - "line": 95 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for use of `Vec>` where T: Sized anywhere in the code. Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.\n\n **Why is this bad?** `Vec` already keeps its contents in a separate area on\n the heap. So if you `Box` its contents, you just add another level of indirection.\n\n **Known problems:** Vec> makes sense if T is a large type (see [#3530](https://github.com/rust-lang/rust-clippy/issues/3530),\n 1st comment).\n\n **Example:**\n ```rust\n struct X {\n values: Vec>,\n }\n ```\n\n Better:\n\n ```rust\n struct X {\n values: Vec,\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "option_option", - "id_span": { - "path": "src/types.rs", - "line": 133 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for use of `Option>` in function signatures and type definitions\n\n **Why is this bad?** `Option<_>` represents an optional value. `Option>`\n represents an optional optional value which is logically the same thing as an optional\n value but has an unneeded extra level of wrapping.\n\n If you have a case where `Some(Some(_))`, `Some(None)` and `None` are distinct cases,\n consider a custom `enum` instead, with clear names for each case.\n\n **Known problems:** None.\n\n **Example**\n ```rust\n fn get_data() -> Option> {\n None\n }\n ```\n\n Better:\n\n ```rust\n pub enum Contents {\n Data(Vec), // Was Some(Some(Vec))\n NotYetFetched, // Was Some(None)\n None, // Was None\n }\n\n fn get_data() -> Contents {\n Contents::None\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "linkedlist", - "id_span": { - "path": "src/types.rs", - "line": 169 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for usage of any `LinkedList`, suggesting to use a `Vec` or a `VecDeque` (formerly called `RingBuf`).\n\n **Why is this bad?** Gankro says:\n\n > The TL;DR of `LinkedList` is that it's built on a massive amount of\n pointers and indirection.\n > It wastes memory, it has terrible cache locality, and is all-around slow.\n `RingBuf`, while\n > \"only\" amortized for push/pop, should be faster in the general case for\n almost every possible\n > workload, and isn't even amortized at all if you can predict the capacity\n you need.\n >\n > `LinkedList`s are only really good if you're doing a lot of merging or\n splitting of lists.\n > This is because they can just mangle some pointers instead of actually\n copying the data. Even\n > if you're doing a lot of insertion in the middle of the list, `RingBuf`\n can still be better\n > because of how expensive it is to seek to the middle of a `LinkedList`.\n\n **Known problems:** False positives – the instances where using a\n `LinkedList` makes sense are few and far between, but they can still happen.\n\n **Example:**\n ```rust\n # use std::collections::LinkedList;\n let x: LinkedList = LinkedList::new();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "borrowed_box", - "id_span": { - "path": "src/types.rs", - "line": 193 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for use of `&Box` anywhere in the code. Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.\n\n **Why is this bad?** Any `&Box` can also be a `&T`, which is more\n general.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n fn foo(bar: &Box) { ... }\n ```\n\n Better:\n\n ```rust,ignore\n fn foo(bar: &T) { ... }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "redundant_allocation", - "id_span": { - "path": "src/types.rs", - "line": 217 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for use of redundant allocations anywhere in the code.\n **Why is this bad?** Expressions such as `Rc<&T>`, `Rc>`, `Rc>`, `Box<&T>`\n add an unnecessary level of indirection.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::rc::Rc;\n fn foo(bar: Rc<&usize>) {}\n ```\n\n Better:\n\n ```rust\n fn foo(bar: &usize) {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "rc_buffer", - "id_span": { - "path": "src/types.rs", - "line": 248 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for `Rc` and `Arc` when `T` is a mutable buffer type such as `String` or `Vec`.\n **Why is this bad?** Expressions such as `Rc` usually have no advantage over `Rc`, since\n it is larger and involves an extra level of indirection, and doesn't implement `Borrow`.\n\n While mutating a buffer type would still be possible with `Rc::get_mut()`, it only\n works if there are no additional references yet, which usually defeats the purpose of\n enclosing it in a shared ownership type. Instead, additionally wrapping the inner\n type with an interior mutable container (such as `RefCell` or `Mutex`) would normally\n be used.\n\n **Known problems:** This pattern can be desirable to avoid the overhead of a `RefCell` or `Mutex` for\n cases where mutation only happens before there are any additional references.\n\n **Example:**\n ```rust,ignore\n # use std::rc::Rc;\n fn foo(interned: Rc) { ... }\n ```\n\n Better:\n\n ```rust,ignore\n fn foo(interned: Rc) { ... }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "let_unit_value", - "id_span": { - "path": "src/types.rs", - "line": 768 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for binding a unit value.\n **Why is this bad?** A unit value cannot usefully be used anywhere. So\n binding one is kind of pointless.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = {\n 1;\n };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unit_cmp", - "id_span": { - "path": "src/types.rs", - "line": 849 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for comparisons to unit. This includes all binary comparisons (like `==` and `<`) and asserts.\n\n **Why is this bad?** Unit is always equal to itself, and thus is just a\n clumsily written constant. Mostly this happens when someone accidentally\n adds semicolons at the end of the operands.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn foo() {};\n # fn bar() {};\n # fn baz() {};\n if {\n foo();\n } == {\n bar();\n } {\n baz();\n }\n ```\n is equal to\n ```rust\n # fn foo() {};\n # fn bar() {};\n # fn baz() {};\n {\n foo();\n bar();\n baz();\n }\n ```\n\n For asserts:\n ```rust\n # fn foo() {};\n # fn bar() {};\n assert_eq!({ foo(); }, { bar(); });\n ```\n will always succeed\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unit_arg", - "id_span": { - "path": "src/types.rs", - "line": 922 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for passing a unit value as an argument to a function without using a unit literal (`()`).\n\n **Why is this bad?** This is likely the result of an accidental semicolon.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n foo({\n let a = bar();\n baz(a);\n })\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MachineApplicable" - } - }, - { - "id": "cast_precision_loss", - "id_span": { - "path": "src/types.rs", - "line": 1158 - }, - "group": "clippy::pedantic", - "docs": " **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\n rounding errors. This possible rounding is to be expected, so this lint is\n `Allow` by default.\n\n Basically, this warns on casting any integer with 32 or more bits to `f32`\n or any 64-bit integer to `f64`.\n\n **Why is this bad?** It's not bad at all. But in some applications it can be\n helpful to know where precision loss can take place. This lint can help find\n those places in the code.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = u64::MAX;\n x as f64;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "cast_sign_loss", - "id_span": { - "path": "src/types.rs", - "line": 1179 - }, - "group": "clippy::pedantic", - "docs": " **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,\n which can be quite surprising in practice. However, as the cast works as\n defined, this lint is `Allow` by default.\n\n **Why is this bad?** Possibly surprising results. You can activate this lint\n as a one-time check to see where numerical wrapping can arise.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let y: i8 = -1;\n y as u128; // will return 18446744073709551615\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "cast_possible_truncation", - "id_span": { - "path": "src/types.rs", - "line": 1201 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for casts between numerical types that may truncate large values. This is expected behavior, so the cast is `Allow` by\n default.\n\n **Why is this bad?** In some problem domains, it is good practice to avoid\n truncation. This lint can be activated to help assess where additional\n checks could be beneficial.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn as_u8(x: u64) -> u8 {\n x as u8\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "cast_possible_wrap", - "id_span": { - "path": "src/types.rs", - "line": 1224 - }, - "group": "clippy::pedantic", - "docs": " **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,\n i.e., nothing is changed at the bit level, and the binary representation of\n the value is reinterpreted. This can cause wrapping if the value is too big\n for the target signed type. However, the cast works as defined, so this lint\n is `Allow` by default.\n\n **Why is this bad?** While such a cast is not bad in itself, the results can\n be surprising when this is not the intended behavior, as demonstrated by the\n example below.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n u32::MAX as i32; // will yield a value of `-1`\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "cast_lossless", - "id_span": { - "path": "src/types.rs", - "line": 1256 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for casts between numerical types that may be replaced by safe conversion functions.\n\n **Why is this bad?** Rust's `as` keyword will perform many kinds of\n conversions, including silently lossy conversions. Conversion functions such\n as `i32::from` will only perform lossless conversions. Using the conversion\n functions prevents conversions from turning into silent lossy conversions if\n the types of the input expressions ever change, and make it easier for\n people reading the code to know that the conversion is lossless.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn as_u64(x: u8) -> u64 {\n x as u64\n }\n ```\n\n Using `::from` would look like this:\n\n ```rust\n fn as_u64(x: u8) -> u64 {\n u64::from(x)\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unnecessary_cast", - "id_span": { - "path": "src/types.rs", - "line": 1281 - }, - "group": "clippy::complexity", - "docs": " **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.\n\n **Why is this bad?** It's just unnecessary.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let _ = 2i32 as i32;\n let _ = 0.5 as f32;\n ```\n\n Better:\n\n ```rust\n let _ = 2_i32;\n let _ = 0.5_f32;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "cast_ptr_alignment", - "id_span": { - "path": "src/types.rs", - "line": 1305 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for casts, using `as` or `pointer::cast`, from a less-strictly-aligned pointer to a more-strictly-aligned pointer\n\n **Why is this bad?** Dereferencing the resulting pointer may be undefined\n behavior.\n\n **Known problems:** Using `std::ptr::read_unaligned` and `std::ptr::write_unaligned` or similar\n on the resulting pointer is fine. Is over-zealous: Casts with manual alignment checks or casts like\n u64-> u8 -> u16 can be fine. Miri is able to do a more in-depth analysis.\n\n **Example:**\n ```rust\n let _ = (&1u8 as *const u8) as *const u16;\n let _ = (&mut 1u8 as *mut u8) as *mut u16;\n\n (&1u8 as *const u8).cast::();\n (&mut 1u8 as *mut u8).cast::();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "fn_to_numeric_cast", - "id_span": { - "path": "src/types.rs", - "line": 1332 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for casts of function pointers to something other than usize\n **Why is this bad?**\n Casting a function pointer to anything other than usize/isize is not portable across\n architectures, because you end up losing bits if the target type is too small or end up with a\n bunch of extra bits that waste space and add more instructions to the final binary than\n strictly necessary for the problem\n\n Casting to isize also doesn't make sense since there are no signed addresses.\n\n **Example**\n\n ```rust\n // Bad\n fn fun() -> i32 { 1 }\n let a = fun as i64;\n\n // Good\n fn fun2() -> i32 { 1 }\n let a = fun2 as usize;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "fn_to_numeric_cast_with_truncation", - "id_span": { - "path": "src/types.rs", - "line": 1362 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for casts of a function pointer to a numeric type not wide enough to store address.\n\n **Why is this bad?**\n Such a cast discards some bits of the function's address. If this is intended, it would be more\n clearly expressed by casting to usize first, then casting the usize to the intended type (with\n a comment) to perform the truncation.\n\n **Example**\n\n ```rust\n // Bad\n fn fn1() -> i16 {\n 1\n };\n let _ = fn1 as i32;\n\n // Better: Cast to usize first, then comment with the reason for the truncation\n fn fn2() -> i16 {\n 1\n };\n let fn_ptr = fn2 as usize;\n let fn_ptr_truncated = fn_ptr as i32;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "type_complexity", - "id_span": { - "path": "src/types.rs", - "line": 1897 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for types used in structs, parameters and `let` declarations above a certain complexity threshold.\n\n **Why is this bad?** Too complex types make the code less readable. Consider\n using a `type` definition to simplify them.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::rc::Rc;\n struct Foo {\n inner: Rc>>>,\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "char_lit_as_u8", - "id_span": { - "path": "src/types.rs", - "line": 2068 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for expressions where a character literal is cast to `u8` and suggests using a byte literal instead.\n\n **Why is this bad?** In general, casting values to smaller types is\n error-prone and should be avoided where possible. In the particular case of\n converting a character literal to u8, it is easy to avoid by just using a\n byte literal instead. As an added bonus, `b'a'` is even slightly shorter\n than `'a' as u8`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n 'x' as u8\n ```\n\n A better version, using the byte literal:\n\n ```rust,ignore\n b'x'\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "absurd_extreme_comparisons", - "id_span": { - "path": "src/types.rs", - "line": 2133 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for comparisons where one side of the relation is either the minimum or maximum value for its type and warns if it involves a\n case that is always true or always false. Only integer and boolean types are\n checked.\n\n **Why is this bad?** An expression like `min <= x` may misleadingly imply\n that it is possible for `x` to be less than the minimum. Expressions like\n `max < x` are probably mistakes.\n\n **Known problems:** For `usize` the size of the current compile target will\n be assumed (e.g., 64 bits on 64 bit systems). This means code that uses such\n a comparison to detect target pointer width will trigger this lint. One can\n use `mem::sizeof` and compare its value or conditional compilation\n attributes\n like `#[cfg(target_pointer_width = \"64\")] ..` instead.\n\n **Example:**\n\n ```rust\n let vec: Vec = Vec::new();\n if vec.len() <= 0 {}\n if 100 > i32::MAX {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "invalid_upcast_comparisons", - "id_span": { - "path": "src/types.rs", - "line": 2294 - }, - "group": "clippy::pedantic", - "docs": " **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\n necessary. Only integer types are checked.\n\n **Why is this bad?** An expression like `let x : u8 = ...; (x as u32) > 300`\n will mistakenly imply that it is possible for `x` to be outside the range of\n `u8`.\n\n **Known problems:**\n https://github.com/rust-lang/rust-clippy/issues/886\n\n **Example:**\n ```rust\n let x: u8 = 1;\n (x as u32) > 300;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "implicit_hasher", - "id_span": { - "path": "src/types.rs", - "line": 2515 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for public `impl` or `fn` missing generalization over different hashers and implicitly defaulting to the default hashing\n algorithm (`SipHash`).\n\n **Why is this bad?** `HashMap` or `HashSet` with custom hashers cannot be\n used with them.\n\n **Known problems:** Suggestions for replacing constructors can contain\n false-positives. Also applying suggestions can require modification of other\n pieces of code, possibly including external crates.\n\n **Example:**\n ```rust\n # use std::collections::HashMap;\n # use std::hash::{Hash, BuildHasher};\n # trait Serialize {};\n impl Serialize for HashMap { }\n\n pub fn foo(map: &mut HashMap) { }\n ```\n could be rewritten as\n ```rust\n # use std::collections::HashMap;\n # use std::hash::{Hash, BuildHasher};\n # trait Serialize {};\n impl Serialize for HashMap { }\n\n pub fn foo(map: &mut HashMap) { }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "cast_ref_to_mut", - "id_span": { - "path": "src/types.rs", - "line": 2866 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for casts of `&T` to `&mut T` anywhere in the code.\n **Why is this bad?** It’s basically guaranteed to be undefined behaviour.\n `UnsafeCell` is the only way to obtain aliasable data that is considered\n mutable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n fn x(r: &i32) {\n unsafe {\n *(r as *const _ as *mut _) += 1;\n }\n }\n ```\n\n Instead consider using interior mutability types.\n\n ```rust\n use std::cell::UnsafeCell;\n\n fn x(r: &UnsafeCell) {\n unsafe {\n *r.get() += 1;\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "ptr_as_ptr", - "id_span": { - "path": "src/types.rs", - "line": 2922 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for `as` casts between raw pointers without changing its mutability,\n namely `*const T` to `*const U` and `*mut T` to `*mut U`.\n\n **Why is this bad?**\n Though `as` casts between raw pointers is not terrible, `pointer::cast` is safer because\n it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let ptr: *const u32 = &42_u32;\n let mut_ptr: *mut u32 = &mut 42_u32;\n let _ = ptr as *const i32;\n let _ = mut_ptr as *mut i32;\n ```\n Use instead:\n ```rust\n let ptr: *const u32 = &42_u32;\n let mut_ptr: *mut u32 = &mut 42_u32;\n let _ = ptr.cast::();\n let _ = mut_ptr.cast::();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "undropped_manually_drops", - "id_span": { - "path": "src/undropped_manually_drops.rs", - "line": 27 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`.\n **Why is this bad?** The safe `drop` function does not drop the inner value of a `ManuallyDrop`.\n\n **Known problems:** Does not catch cases if the user binds `std::mem::drop`\n to a different name and calls it that way.\n\n **Example:**\n\n ```rust\n struct S;\n drop(std::mem::ManuallyDrop::new(S));\n ```\n Use instead:\n ```rust\n struct S;\n unsafe {\n std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S));\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "invisible_characters", - "id_span": { - "path": "src/unicode.rs", - "line": 20 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for invisible Unicode characters in the code.\n **Why is this bad?** Having an invisible character in the code makes for all\n sorts of April fools, but otherwise is very much frowned upon.\n\n **Known problems:** None.\n\n **Example:** You don't see it, but there may be a zero-width space or soft hyphen\n some­where in this text.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "non_ascii_literal", - "id_span": { - "path": "src/unicode.rs", - "line": 44 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for non-ASCII characters in string literals.\n **Why is this bad?** Yeah, we know, the 90's called and wanted their charset\n back. Even so, there still are editors and other programs out there that\n don't work well with Unicode. So if the code is meant to be used\n internationally, on multiple operating systems, or has other portability\n requirements, activating this lint could be useful.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = String::from(\"€\");\n ```\n Could be written as:\n ```rust\n let x = String::from(\"\\u{20ac}\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unicode_not_nfc", - "id_span": { - "path": "src/unicode.rs", - "line": 61 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for string literals that contain Unicode in a form that is not equal to its\n [NFC-recomposition](http://www.unicode.org/reports/tr15/#Norm_Forms).\n\n **Why is this bad?** If such a string is compared to another, the results\n may be surprising.\n\n **Known problems** None.\n\n **Example:** You may not see it, but \"à\"\" and \"à\"\" aren't the same string. The\n former when escaped is actually `\"a\\u{300}\"` while the latter is `\"\\u{e0}\"`.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unit_return_expecting_ord", - "id_span": { - "path": "src/unit_return_expecting_ord.rs", - "line": 30 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for functions that expect closures of type Fn(...) -> Ord where the implemented closure returns the unit type.\n The lint also suggests to remove the semi-colon at the end of the statement if present.\n\n **Why is this bad?** Likely, returning the unit type is unintentional, and\n could simply be caused by an extra semi-colon. Since () implements Ord\n it doesn't cause a compilation error.\n This is the same reasoning behind the unit_cmp lint.\n\n **Known problems:** If returning unit is intentional, then there is no\n way of specifying this without triggering needless_return lint\n\n **Example:**\n\n ```rust\n let mut twins = vec!((1, 1), (2, 2));\n twins.sort_by_key(|x| { x.1; });\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "fn_address_comparisons", - "id_span": { - "path": "src/unnamed_address.rs", - "line": 27 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for comparisons with an address of a function item.\n **Why is this bad?** Function item address is not guaranteed to be unique and could vary\n between different code generation units. Furthermore different function items could have\n the same address after being merged together.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n type F = fn();\n fn a() {}\n let f: F = a;\n if f == a {\n // ...\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "vtable_address_comparisons", - "id_span": { - "path": "src/unnamed_address.rs", - "line": 51 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for comparisons with an address of a trait vtable.\n **Why is this bad?** Comparing trait objects pointers compares an vtable addresses which\n are not guaranteed to be unique and could vary between different code generation units.\n Furthermore vtables for different types could have the same address after being merged\n together.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,ignore\n let a: Rc = ...\n let b: Rc = ...\n if Rc::ptr_eq(&a, &b) {\n ...\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unnecessary_sort_by", - "id_span": { - "path": "src/unnecessary_sort_by.rs", - "line": 41 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Detects uses of `Vec::sort_by` passing in a closure\n which compares the two arguments, either directly or indirectly.\n\n **Why is this bad?**\n It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if\n possible) than to use `Vec::sort_by` and a more complicated\n closure.\n\n **Known problems:**\n If the suggested `Vec::sort_by_key` uses Reverse and it isn't already\n imported by a use statement, then it will need to be added manually.\n\n **Example:**\n\n ```rust\n # struct A;\n # impl A { fn foo(&self) {} }\n # let mut vec: Vec = Vec::new();\n vec.sort_by(|a, b| a.foo().cmp(&b.foo()));\n ```\n Use instead:\n ```rust\n # struct A;\n # impl A { fn foo(&self) {} }\n # let mut vec: Vec = Vec::new();\n vec.sort_by_key(|a| a.foo());\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unnecessary_wraps", - "id_span": { - "path": "src/unnecessary_wraps.rs", - "line": 50 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for private functions that only return `Ok` or `Some`.\n **Why is this bad?** It is not meaningful to wrap values when no `None` or `Err` is returned.\n\n **Known problems:** There can be false positives if the function signature is designed to\n fit some external requirement.\n\n **Example:**\n\n ```rust\n fn get_cool_number(a: bool, b: bool) -> Option {\n if a && b {\n return Some(50);\n }\n if a {\n Some(0)\n } else {\n Some(10)\n }\n }\n ```\n Use instead:\n ```rust\n fn get_cool_number(a: bool, b: bool) -> i32 {\n if a && b {\n return 50;\n }\n if a {\n 0\n } else {\n 10\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "unnested_or_patterns", - "id_span": { - "path": "src/unnested_or_patterns.rs", - "line": 47 - }, - "group": "clippy::pedantic", - "docs": " **What it does:**\n Checks for unnested or-patterns, e.g., `Some(0) | Some(2)` and\n suggests replacing the pattern with a nested one, `Some(0 | 2)`.\n\n Another way to think of this is that it rewrites patterns in\n *disjunctive normal form (DNF)* into *conjunctive normal form (CNF)*.\n\n **Why is this bad?**\n\n In the example above, `Some` is repeated, which unncessarily complicates the pattern.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n fn main() {\n if let Some(0) | Some(2) = Some(0) {}\n }\n ```\n Use instead:\n ```rust\n #![feature(or_patterns)]\n\n fn main() {\n if let Some(0 | 2) = Some(0) {}\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unsafe_removed_from_name", - "id_span": { - "path": "src/unsafe_removed_from_name.rs", - "line": 24 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for imports that remove \"unsafe\" from an item's name.\n\n **Why is this bad?** Renaming makes it less clear which traits and\n structures are unsafe.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n use std::cell::{UnsafeCell as TotallySafeCell};\n\n extern crate crossbeam;\n use crossbeam::{spawn_unsafe as spawn};\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unused_io_amount", - "id_span": { - "path": "src/unused_io_amount.rs", - "line": 28 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for unused written/read amount.\n **Why is this bad?** `io::Write::write(_vectored)` and\n `io::Read::read(_vectored)` are not guaranteed to\n process the entire buffer. They return how many bytes were processed, which\n might be smaller\n than a given buffer's length. If you don't need to deal with\n partial-write/read, use\n `write_all`/`read_exact` instead.\n\n **Known problems:** Detects only common patterns.\n\n **Example:**\n ```rust,ignore\n use std::io;\n fn foo(w: &mut W) -> io::Result<()> {\n // must be `w.write_all(b\"foo\")?;`\n w.write(b\"foo\")?;\n Ok(())\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unused_self", - "id_span": { - "path": "src/unused_self.rs", - "line": 33 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks methods that contain a `self` argument but don't use it\n **Why is this bad?** It may be clearer to define the method as an associated function instead\n of an instance method if it doesn't require `self`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n struct A;\n impl A {\n fn method(&self) {}\n }\n ```\n\n Could be written:\n\n ```rust,ignore\n struct A;\n impl A {\n fn method() {}\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unused_unit", - "id_span": { - "path": "src/unused_unit.rs", - "line": 27 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for unit (`()`) expressions that can be removed.\n **Why is this bad?** Such expressions add no value, but can make the code\n less readable. Depending on formatting they can make a `break` or `return`\n statement look like a function call.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn return_unit() -> () {\n ()\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unnecessary_unwrap", - "id_span": { - "path": "src/unwrap.rs", - "line": 40 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for calls of `unwrap[_err]()` that cannot fail.\n **Why is this bad?** Using `if let` or `match` is more idiomatic.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n # let option = Some(0);\n # fn do_something_with(_x: usize) {}\n if option.is_some() {\n do_something_with(option.unwrap())\n }\n ```\n\n Could be written:\n\n ```rust\n # let option = Some(0);\n # fn do_something_with(_x: usize) {}\n if let Some(value) = option {\n do_something_with(value)\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "panicking_unwrap", - "id_span": { - "path": "src/unwrap.rs", - "line": 63 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for calls of `unwrap[_err]()` that will always fail.\n **Why is this bad?** If panicking is desired, an explicit `panic!()` should be used.\n\n **Known problems:** This lint only checks `if` conditions not assignments.\n So something like `let x: Option<()> = None; x.unwrap();` will not be recognized.\n\n **Example:**\n ```rust\n # let option = Some(0);\n # fn do_something_with(_x: usize) {}\n if option.is_none() {\n do_something_with(option.unwrap())\n }\n ```\n\n This code will always panic. The if condition should probably be inverted.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unwrap_in_result", - "id_span": { - "path": "src/unwrap_in_result.rs", - "line": 47 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for functions of type Result that contain `expect()` or `unwrap()`\n **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.\n\n **Known problems:** This can cause false positives in functions that handle both recoverable and non recoverable errors.\n\n **Example:**\n Before:\n ```rust\n fn divisible_by_3(i_str: String) -> Result<(), String> {\n let i = i_str\n .parse::()\n .expect(\"cannot divide the input by three\");\n\n if i % 3 != 0 {\n Err(\"Number is not divisible by 3\")?\n }\n\n Ok(())\n }\n ```\n\n After:\n ```rust\n fn divisible_by_3(i_str: String) -> Result<(), String> {\n let i = i_str\n .parse::()\n .map_err(|e| format!(\"cannot divide the input by three: {}\", e))?;\n\n if i % 3 != 0 {\n Err(\"Number is not divisible by 3\")?\n }\n\n Ok(())\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "upper_case_acronyms", - "id_span": { - "path": "src/upper_case_acronyms.rs", - "line": 35 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for fully capitalized names and optionally names containing a capitalized acronym.\n **Why is this bad?** In CamelCase, acronyms count as one word.\n See [naming conventions](https://rust-lang.github.io/api-guidelines/naming.html#casing-conforms-to-rfc-430-c-case)\n for more.\n\n By default, the lint only triggers on fully-capitalized names.\n You can use the `upper-case-acronyms-aggressive: true` config option to enable linting\n on all camel case names\n\n **Known problems:** When two acronyms are contiguous, the lint can't tell where\n the first acronym ends and the second starts, so it suggests to lowercase all of\n the letters in the second acronym.\n\n **Example:**\n\n ```rust\n struct HTTPResponse;\n ```\n Use instead:\n ```rust\n struct HttpResponse;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "use_self", - "id_span": { - "path": "src/use_self.rs", - "line": 53 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for unnecessary repetition of structure name when a replacement with `Self` is applicable.\n\n **Why is this bad?** Unnecessary repetition. Mixed use of `Self` and struct\n name\n feels inconsistent.\n\n **Known problems:**\n - Unaddressed false negative in fn bodies of trait implementations\n - False positive with assotiated types in traits (#4140)\n\n **Example:**\n\n ```rust\n struct Foo {}\n impl Foo {\n fn new() -> Foo {\n Foo {}\n }\n }\n ```\n could be\n ```rust\n struct Foo {}\n impl Foo {\n fn new() -> Self {\n Self {}\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "useless_conversion", - "id_span": { - "path": "src/useless_conversion.rs", - "line": 32 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` calls which uselessly convert to the same type.\n\n **Why is this bad?** Redundant code.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n // format!() returns a `String`\n let s: String = format!(\"hello\").into();\n\n // Good\n let s: String = format!(\"hello\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "useless_vec", - "id_span": { - "path": "src/vec.rs", - "line": 36 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for usage of `&vec![..]` when using `&[..]` would be possible.\n\n **Why is this bad?** This is less efficient.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn foo(my_vec: &[u8]) {}\n\n // Bad\n foo(&vec![1, 2]);\n\n // Good\n foo(&[1, 2]);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "vec_init_then_push", - "id_span": { - "path": "src/vec_init_then_push.rs", - "line": 32 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for calls to `push` immediately after creating a new `Vec`.\n **Why is this bad?** The `vec![]` macro is both more performant and easier to read than\n multiple `push` calls.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let mut v = Vec::new();\n v.push(0);\n ```\n Use instead:\n ```rust\n let v = vec![0];\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "HasPlaceholders" - } - }, - { - "id": "vec_resize_to_zero", - "id_span": { - "path": "src/vec_resize_to_zero.rs", - "line": 24 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Finds occurrences of `Vec::resize(0, an_int)`\n **Why is this bad?** This is probably an argument inversion mistake.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n vec!(1, 2, 3, 4, 5).resize(0, 5)\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "verbose_file_reads", - "id_span": { - "path": "src/verbose_file_reads.rs", - "line": 29 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for use of File::read_to_end and File::read_to_string.\n **Why is this bad?** `fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values.\n See also: [fs::read docs](https://doc.rust-lang.org/std/fs/fn.read.html), [fs::read_to_string docs](https://doc.rust-lang.org/std/fs/fn.read_to_string.html)\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,no_run\n # use std::io::Read;\n # use std::fs::File;\n let mut f = File::open(\"foo.txt\").unwrap();\n let mut bytes = Vec::new();\n f.read_to_end(&mut bytes).unwrap();\n ```\n Can be written more concisely as\n ```rust,no_run\n # use std::fs;\n let mut bytes = fs::read(\"foo.txt\").unwrap();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "wildcard_dependencies", - "id_span": { - "path": "src/wildcard_dependencies.rs", - "line": 24 - }, - "group": "clippy::cargo", - "docs": " **What it does:** Checks for wildcard dependencies in the `Cargo.toml`.\n **Why is this bad?** [As the edition guide says](https://rust-lang-nursery.github.io/edition-guide/rust-2018/cargo-and-crates-io/crates-io-disallows-wildcard-dependencies.html),\n it is highly unlikely that you work with any possible version of your dependency,\n and wildcard dependencies would cause unnecessary breakage in the ecosystem.\n\n **Known problems:** None.\n\n **Example:**\n\n ```toml\n [dependencies]\n regex = \"*\"\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "enum_glob_use", - "id_span": { - "path": "src/wildcard_imports.rs", - "line": 32 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for `use Enum::*`.\n **Why is this bad?** It is usually better style to use the prefixed name of\n an enumeration variant, rather than importing variants.\n\n **Known problems:** Old-style enumerations that prefix the variants are\n still around.\n\n **Example:**\n ```rust,ignore\n // Bad\n use std::cmp::Ordering::*;\n foo(Less);\n\n // Good\n use std::cmp::Ordering;\n foo(Ordering::Less)\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "wildcard_imports", - "id_span": { - "path": "src/wildcard_imports.rs", - "line": 83 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for wildcard imports `use _::*`.\n **Why is this bad?** wildcard imports can pollute the namespace. This is especially bad if\n you try to import something through a wildcard, that already has been imported by name from\n a different source:\n\n ```rust,ignore\n use crate1::foo; // Imports a function named foo\n use crate2::*; // Has a function named foo\n\n foo(); // Calls crate1::foo\n ```\n\n This can lead to confusing error messages at best and to unexpected behavior at worst.\n\n **Exceptions:**\n\n Wildcard imports are allowed from modules named `prelude`. Many crates (including the standard library)\n provide modules named \"prelude\" specifically designed for wildcard import.\n\n `use super::*` is allowed in test modules. This is defined as any module with \"test\" in the name.\n\n These exceptions can be disabled using the `warn-on-all-wildcard-imports` configuration flag.\n\n **Known problems:** If macros are imported through the wildcard, this macro is not included\n by the suggestion and has to be added by hand.\n\n Applying the suggestion when explicit imports of the things imported with a glob import\n exist, may result in `unused_imports` warnings.\n\n **Example:**\n\n ```rust,ignore\n // Bad\n use crate1::*;\n\n foo();\n ```\n\n ```rust,ignore\n // Good\n use crate1::foo;\n\n foo();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "println_empty_string", - "id_span": { - "path": "src/write.rs", - "line": 33 - }, - "group": "clippy::style", - "docs": " **What it does:** This lint warns when you use `println!(\"\")` to print a newline.\n\n **Why is this bad?** You should use `println!()`, which is simpler.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n println!(\"\");\n\n // Good\n println!();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "print_with_newline", - "id_span": { - "path": "src/write.rs", - "line": 57 - }, - "group": "clippy::style", - "docs": " **What it does:** This lint warns when you use `print!()` with a format string that ends in a newline.\n\n **Why is this bad?** You should use `println!()` instead, which appends the\n newline.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let name = \"World\";\n print!(\"Hello {}!\\n\", name);\n ```\n use println!() instead\n ```rust\n # let name = \"World\";\n println!(\"Hello {}!\", name);\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MachineApplicable" - } - }, - { - "id": "print_stdout", - "id_span": { - "path": "src/write.rs", - "line": 75 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for printing on *stdout*. The purpose of this lint is to catch debugging remnants.\n\n **Why is this bad?** People often print on *stdout* while debugging an\n application and might forget to remove those prints afterward.\n\n **Known problems:** Only catches `print!` and `println!` calls.\n\n **Example:**\n ```rust\n println!(\"Hello world!\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "print_stderr", - "id_span": { - "path": "src/write.rs", - "line": 93 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for printing on *stderr*. The purpose of this lint is to catch debugging remnants.\n\n **Why is this bad?** People often print on *stderr* while debugging an\n application and might forget to remove those prints afterward.\n\n **Known problems:** Only catches `eprint!` and `eprintln!` calls.\n\n **Example:**\n ```rust\n eprintln!(\"Hello world!\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "use_debug", - "id_span": { - "path": "src/write.rs", - "line": 110 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for use of `Debug` formatting. The purpose of this lint is to catch debugging remnants.\n\n **Why is this bad?** The purpose of the `Debug` trait is to facilitate\n debugging Rust code. It should not be used in user-facing output.\n\n **Example:**\n ```rust\n # let foo = \"bar\";\n println!(\"{:?}\", foo);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "print_literal", - "id_span": { - "path": "src/write.rs", - "line": 133 - }, - "group": "clippy::style", - "docs": " **What it does:** This lint warns about the use of literals as `print!`/`println!` args.\n **Why is this bad?** Using literals as `println!` args is inefficient\n (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary\n (i.e., just put the literal in the format string)\n\n **Known problems:** Will also warn with macro calls as arguments that expand to literals\n -- e.g., `println!(\"{}\", env!(\"FOO\"))`.\n\n **Example:**\n ```rust\n println!(\"{}\", \"foo\");\n ```\n use the literal without formatting:\n ```rust\n println!(\"foo\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "writeln_empty_string", - "id_span": { - "path": "src/write.rs", - "line": 156 - }, - "group": "clippy::style", - "docs": " **What it does:** This lint warns when you use `writeln!(buf, \"\")` to print a newline.\n\n **Why is this bad?** You should use `writeln!(buf)`, which is simpler.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::fmt::Write;\n # let mut buf = String::new();\n // Bad\n writeln!(buf, \"\");\n\n // Good\n writeln!(buf);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "write_with_newline", - "id_span": { - "path": "src/write.rs", - "line": 182 - }, - "group": "clippy::style", - "docs": " **What it does:** This lint warns when you use `write!()` with a format string that\n ends in a newline.\n\n **Why is this bad?** You should use `writeln!()` instead, which appends the\n newline.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::fmt::Write;\n # let mut buf = String::new();\n # let name = \"World\";\n // Bad\n write!(buf, \"Hello {}!\\n\", name);\n\n // Good\n writeln!(buf, \"Hello {}!\", name);\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MachineApplicable" - } - }, - { - "id": "write_literal", - "id_span": { - "path": "src/write.rs", - "line": 207 - }, - "group": "clippy::style", - "docs": " **What it does:** This lint warns about the use of literals as `write!`/`writeln!` args.\n **Why is this bad?** Using literals as `writeln!` args is inefficient\n (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary\n (i.e., just put the literal in the format string)\n\n **Known problems:** Will also warn with macro calls as arguments that expand to literals\n -- e.g., `writeln!(buf, \"{}\", env!(\"FOO\"))`.\n\n **Example:**\n ```rust\n # use std::fmt::Write;\n # let mut buf = String::new();\n // Bad\n writeln!(buf, \"{}\", \"foo\");\n\n // Good\n writeln!(buf, \"foo\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "zero_divided_by_zero", - "id_span": { - "path": "src/zero_div_zero.rs", - "line": 23 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for `0.0 / 0.0`.\n **Why is this bad?** It's less readable than `f32::NAN` or `f64::NAN`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n let nan = 0.0f32 / 0.0;\n\n // Good\n let nan = f32::NAN;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "zero_sized_map_values", - "id_span": { - "path": "src/zero_sized_map_values.rs", - "line": 37 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for maps with zero-sized value types anywhere in the code.\n **Why is this bad?** Since there is only a single value for a zero-sized type, a map\n containing zero sized values is effectively a set. Using a set in that case improves\n readability and communicates intent more clearly.\n\n **Known problems:**\n * A zero-sized type cannot be recovered later if it contains private fields.\n * This lints the signature of public items\n\n **Example:**\n\n ```rust\n # use std::collections::HashMap;\n fn unique_words(text: &str) -> HashMap<&str, ()> {\n todo!();\n }\n ```\n Use instead:\n ```rust\n # use std::collections::HashSet;\n fn unique_words(text: &str) -> HashSet<&str> {\n todo!();\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - } -] diff --git a/tests/dogfood.rs b/tests/dogfood.rs index b0da0bfcc3576..604968ad8b0c5 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -41,10 +41,6 @@ fn dogfood_clippy() { command.args(&["-D", "clippy::internal"]); } - if cfg!(feature = "metadata-collector-lint") { - command.args(&["-D", "clippy::internal"]); - } - let output = command.output().unwrap(); println!("status: {}", output.status); diff --git a/tests/ui-internal/metadata-collector/track_applicability_value.rs b/tests/ui-internal/metadata-collector/track_applicability_value.rs deleted file mode 100644 index b0f59e5cf8a76..0000000000000 --- a/tests/ui-internal/metadata-collector/track_applicability_value.rs +++ /dev/null @@ -1,50 +0,0 @@ -#![deny(clippy::internal)] -#![feature(rustc_private)] - -extern crate rustc_ast; -extern crate rustc_errors; -extern crate rustc_lint; -extern crate rustc_session; -extern crate rustc_span; - -use rustc_ast::ast::Expr; -use rustc_errors::{Applicability, DiagnosticBuilder}; -use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; - -fn producer_fn() -> Applicability { - Applicability::MachineApplicable -} - -fn modifier_fn(applicability: &mut Applicability) { - if let Applicability::MaybeIncorrect = applicability { - *applicability = Applicability::HasPlaceholders; - } -} - -fn consumer_fn(_applicability: Applicability) {} - -struct Muh; - -impl Muh { - fn producer_method() -> Applicability { - Applicability::MachineApplicable - } -} - -fn main() { - let mut applicability = producer_fn(); - applicability = Applicability::MachineApplicable; - applicability = Muh::producer_method(); - - applicability = if true { - Applicability::HasPlaceholders - } else { - Applicability::MaybeIncorrect - }; - - modifier_fn(&mut applicability); - - consumer_fn(applicability); -} From c1fa1102d484b74cb7bc425994fa4246dd690ad3 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Fri, 12 Mar 2021 21:52:01 +0100 Subject: [PATCH 58/67] ENABLE_METADATA_COLLECTION env-value to disable default metadata collection --- clippy_lints/src/lib.rs | 6 +++++- .../utils/internal_lints/metadata_collector.rs | 18 ------------------ tests/dogfood.rs | 2 ++ 3 files changed, 7 insertions(+), 19 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7668d4dd0b105..1a74f641554db 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1005,7 +1005,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass); } #[cfg(feature = "metadata-collector-lint")] - store.register_late_pass(|| box utils::internal_lints::metadata_collector::MetadataCollector::default()); + { + if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) { + store.register_late_pass(|| box utils::internal_lints::metadata_collector::MetadataCollector::default()); + } + } store.register_late_pass(|| box utils::author::Author); store.register_late_pass(|| box await_holding_invalid::AwaitHolding); diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 37e90bf8686f4..07abae31386b2 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -148,24 +148,6 @@ struct LintMetadata { applicability: Option, } -// impl Ord for LintMetadata { -// fn cmp(&self, other: &Self) -> Ordering { -// self.id.cmp(&other.id) -// } -// } -// -// impl PartialOrd for LintMetadata { -// fn partial_cmp(&self, other: &Self) -> Option { -// Some(self.cmp(other)) -// } -// } -// -// impl PartialEq for LintMetadata { -// fn eq(&self, other: &Self) -> bool { -// self.id == other.id -// } -// } - impl LintMetadata { fn new(id: String, id_span: SerializableSpan, group: String, docs: String) -> Self { Self { diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 604968ad8b0c5..6524fd4706ce1 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -22,12 +22,14 @@ fn dogfood_clippy() { return; } let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let enable_metadata_collection = std::env::var("ENABLE_METADATA_COLLECTION").unwrap_or_else(|_| "0".to_string()); let mut command = Command::new(&*CLIPPY_PATH); command .current_dir(root_dir) .env("CLIPPY_DOGFOOD", "1") .env("CARGO_INCREMENTAL", "0") + .env("ENABLE_METADATA_COLLECTION", &enable_metadata_collection) .arg("clippy") .arg("--all-targets") .arg("--all-features") From 62cafe2c02440cb1cc7cc9cbf1abe63942a19e51 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Fri, 12 Mar 2021 20:59:41 +0100 Subject: [PATCH 59/67] Applying PR suggestions --- .../src/utils/internal_lints/metadata_collector.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 07abae31386b2..b7bb557f9579c 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -32,7 +32,7 @@ use crate::utils::{ }; /// This is the output file of the lint collector. -const OUTPUT_FILE: &str = "../metadata_collection.json"; +const OUTPUT_FILE: &str = "../util/gh-pages/metadata_collection.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 @@ -62,7 +62,7 @@ const SUGGESTION_DIAGNOSTIC_BUILDER_METHODS: [(&str, bool); 9] = [ ("span_suggestions", true), ]; const SUGGESTION_FUNCTIONS: [&[&str]; 2] = [ - &["clippy_utils", "diagnostics", "mutispan_sugg"], + &["clippy_utils", "diagnostics", "multispan_sugg"], &["clippy_utils", "diagnostics", "multispan_sugg_with_applicability"], ]; @@ -226,11 +226,8 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { fn check_item(&mut self, cx: &LateContext<'hir>, item: &'hir Item<'_>) { if_chain! { // item validation - if let ItemKind::Static(ref ty, Mutability::Not, body_id) = item.kind; + if let ItemKind::Static(ref ty, Mutability::Not, _) = item.kind; if is_lint_ref_type(cx, ty); - let expr = &cx.tcx.hir().body(body_id).value; - if let ExprKind::AddrOf(_, _, ref inner_exp) = expr.kind; - if let ExprKind::Struct(_, _, _) = inner_exp.kind; // blacklist check let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase(); if !BLACK_LISTED_LINTS.contains(&lint_name.as_str()); From e0eb29c936602553f7952a4d12c7d193bc3a7513 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Wed, 31 Mar 2021 20:03:38 +0200 Subject: [PATCH 60/67] Applying PR suggestions (mostly typos) Co-authored-by: flip1995 Co-authored-by: phansch --- Cargo.toml | 2 +- .../internal_lints/metadata_collector.rs | 19 ++++++++++--------- clippy_utils/src/diagnostics.rs | 8 ++++++++ 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bdee8e408213f..23a035f9715db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,7 +52,7 @@ rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util" } deny-warnings = [] integration = ["tempfile"] internal-lints = ["clippy_lints/internal-lints"] -metadata-collector-lint = ["clippy_lints/metadata-collector-lint"] +metadata-collector-lint = ["internal-lints", "clippy_lints/metadata-collector-lint"] [package.metadata.rust-analyzer] # This package uses #[feature(rustc_private)] diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index b7bb557f9579c..e85637ca75888 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -27,8 +27,9 @@ use std::io::prelude::*; use std::path::Path; use crate::utils::internal_lints::is_lint_ref_type; -use crate::utils::{ - last_path_segment, match_function_call, match_path, match_type, paths, span_lint, walk_ptrs_ty_depth, +use clippy_utils::{ + diagnostics::span_lint, last_path_segment, match_function_call, match_path, paths, ty::match_type, + ty::walk_ptrs_ty_depth, }; /// This is the output file of the lint collector. @@ -107,7 +108,7 @@ pub struct MetadataCollector { /// /// We use a Heap here to have the lints added in alphabetic order in the export lints: BinaryHeap, - applicability_into: FxHashMap, + applicability_info: FxHashMap, } impl Drop for MetadataCollector { @@ -120,7 +121,7 @@ impl Drop for MetadataCollector { return; } - let mut applicability_info = std::mem::take(&mut self.applicability_into); + 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(); @@ -272,7 +273,7 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { } for (lint_name, applicability, is_multi_part) in emission_info.drain(..) { - let app_info = self.applicability_into.entry(lint_name).or_default(); + let app_info = self.applicability_info.entry(lint_name).or_default(); app_info.applicability = applicability; app_info.is_multi_part_suggestion = is_multi_part; } @@ -354,7 +355,7 @@ fn lint_collection_error_item(cx: &LateContext<'_>, item: &Item<'_>, message: &s cx, INTERNAL_METADATA_COLLECTOR, item.ident.span, - &format!("Metadata collection error for `{}`: {}", item.ident.name, message), + &format!("metadata collection error for `{}`: {}", item.ident.name, message), ); } @@ -569,7 +570,7 @@ impl<'a, 'hir> IsMultiSpanScanner<'a, 'hir> { } /// Add a new single expression suggestion to the counter - fn add_singe_span_suggestion(&mut self) { + fn add_single_span_suggestion(&mut self) { self.suggestion_count += 1; } @@ -604,7 +605,7 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for IsMultiSpanScanner<'a, 'hir> { .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_singe_span_suggestion() + self.add_single_span_suggestion() } }, ExprKind::MethodCall(path, _path_span, arg, _arg_span) => { @@ -616,7 +617,7 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for IsMultiSpanScanner<'a, 'hir> { if *is_multi_part { self.add_multi_part_suggestion(); } else { - self.add_singe_span_suggestion(); + self.add_single_span_suggestion(); } break; } diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index 7f827f1759d2f..a4efae54894fb 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -1,4 +1,12 @@ //! 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; From 2b38399920ad97cc9db61e50549fd4af54d4b38d Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 5 May 2021 14:44:32 -0500 Subject: [PATCH 61/67] Improve eval_order_dependence output --- clippy_lints/src/eval_order_dependence.rs | 2 +- tests/ui/eval_order_dependence.stderr | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/eval_order_dependence.rs b/clippy_lints/src/eval_order_dependence.rs index 762f64fe37ad6..8815ca274943e 100644 --- a/clippy_lints/src/eval_order_dependence.rs +++ b/clippy_lints/src/eval_order_dependence.rs @@ -305,7 +305,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ReadVisitor<'a, 'tcx> { self.cx, EVAL_ORDER_DEPENDENCE, expr.span, - "unsequenced read of a variable", + &format!("unsequenced read of `{}`", self.cx.tcx.hir().name(self.var)), Some(self.write_expr.span), "whether read occurs before this write depends on evaluation order", ); diff --git a/tests/ui/eval_order_dependence.stderr b/tests/ui/eval_order_dependence.stderr index 8f4fa2228f7f4..cf4adbdfa240c 100644 --- a/tests/ui/eval_order_dependence.stderr +++ b/tests/ui/eval_order_dependence.stderr @@ -1,4 +1,4 @@ -error: unsequenced read of a variable +error: unsequenced read of `x` --> $DIR/eval_order_dependence.rs:15:9 | LL | } + x; @@ -11,7 +11,7 @@ note: whether read occurs before this write depends on evaluation order LL | x = 1; | ^^^^^ -error: unsequenced read of a variable +error: unsequenced read of `x` --> $DIR/eval_order_dependence.rs:18:5 | LL | x += { @@ -23,7 +23,7 @@ note: whether read occurs before this write depends on evaluation order LL | x = 20; | ^^^^^^ -error: unsequenced read of a variable +error: unsequenced read of `x` --> $DIR/eval_order_dependence.rs:31:12 | LL | a: x, @@ -35,7 +35,7 @@ note: whether read occurs before this write depends on evaluation order LL | x = 6; | ^^^^^ -error: unsequenced read of a variable +error: unsequenced read of `x` --> $DIR/eval_order_dependence.rs:40:9 | LL | x += { From 7a7b8bd3e885ec97725579d89ba3ac12a1e6f93f Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 5 May 2021 15:02:47 -0500 Subject: [PATCH 62/67] Fix eval_order_dependence async false positive --- clippy_lints/src/eval_order_dependence.rs | 28 +++++++++++------------ tests/ui/eval_order_dependence.rs | 6 +++++ tests/ui/eval_order_dependence.stderr | 16 ++++++------- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/clippy_lints/src/eval_order_dependence.rs b/clippy_lints/src/eval_order_dependence.rs index 8815ca274943e..41acf55dd7d57 100644 --- a/clippy_lints/src/eval_order_dependence.rs +++ b/clippy_lints/src/eval_order_dependence.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id}; +use if_chain::if_chain; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Guard, HirId, Local, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -70,20 +71,19 @@ declare_lint_pass!(EvalOrderDependence => [EVAL_ORDER_DEPENDENCE, DIVERGING_SUB_ impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Find a write to a local variable. - match expr.kind { - ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) => { - if let Some(var) = path_to_local(lhs) { - let mut visitor = ReadVisitor { - cx, - var, - write_expr: expr, - last_expr: expr, - }; - check_for_unsequenced_reads(&mut visitor); - } - }, - _ => {}, - } + let var = if_chain! { + if let ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) = expr.kind; + if let Some(var) = path_to_local(lhs); + if expr.span.desugaring_kind().is_none(); + then { var } else { return; } + }; + let mut visitor = ReadVisitor { + cx, + var, + write_expr: expr, + last_expr: expr, + }; + check_for_unsequenced_reads(&mut visitor); } fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { match stmt.kind { diff --git a/tests/ui/eval_order_dependence.rs b/tests/ui/eval_order_dependence.rs index d806bc6d40102..d742856bc4163 100644 --- a/tests/ui/eval_order_dependence.rs +++ b/tests/ui/eval_order_dependence.rs @@ -1,3 +1,5 @@ +// edition:2018 + #[warn(clippy::eval_order_dependence)] #[allow( unused_assignments, @@ -107,3 +109,7 @@ fn main() { }, ); } + +async fn issue_6925() { + let _ = vec![async { true }.await, async { false }.await]; +} diff --git a/tests/ui/eval_order_dependence.stderr b/tests/ui/eval_order_dependence.stderr index cf4adbdfa240c..35eb85e95a320 100644 --- a/tests/ui/eval_order_dependence.stderr +++ b/tests/ui/eval_order_dependence.stderr @@ -1,48 +1,48 @@ error: unsequenced read of `x` - --> $DIR/eval_order_dependence.rs:15:9 + --> $DIR/eval_order_dependence.rs:17: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:13:9 + --> $DIR/eval_order_dependence.rs:15:9 | LL | x = 1; | ^^^^^ error: unsequenced read of `x` - --> $DIR/eval_order_dependence.rs:18:5 + --> $DIR/eval_order_dependence.rs:20:5 | LL | x += { | ^ | note: whether read occurs before this write depends on evaluation order - --> $DIR/eval_order_dependence.rs:19:9 + --> $DIR/eval_order_dependence.rs:21:9 | LL | x = 20; | ^^^^^^ error: unsequenced read of `x` - --> $DIR/eval_order_dependence.rs:31:12 + --> $DIR/eval_order_dependence.rs:33:12 | LL | a: x, | ^ | note: whether read occurs before this write depends on evaluation order - --> $DIR/eval_order_dependence.rs:33:13 + --> $DIR/eval_order_dependence.rs:35:13 | LL | x = 6; | ^^^^^ error: unsequenced read of `x` - --> $DIR/eval_order_dependence.rs:40:9 + --> $DIR/eval_order_dependence.rs:42:9 | LL | x += { | ^ | note: whether read occurs before this write depends on evaluation order - --> $DIR/eval_order_dependence.rs:41:13 + --> $DIR/eval_order_dependence.rs:43:13 | LL | x = 20; | ^^^^^^ From d66d37303c7074e8e26a0f67bba5a36cd6b12e29 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 5 May 2021 16:05:16 -0500 Subject: [PATCH 63/67] Fix unnecessary_filter_map false positive --- .../src/methods/unnecessary_filter_map.rs | 40 ++++++++++--------- tests/ui/unnecessary_filter_map.rs | 4 ++ 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index b61c4ffe9b3ae..8b66587bfd162 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -6,6 +6,7 @@ use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; +use rustc_middle::ty::{self, TyS}; use rustc_span::sym; use super::UNNECESSARY_FILTER_MAP; @@ -28,25 +29,28 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr< found_mapping |= return_visitor.found_mapping; found_filtering |= return_visitor.found_filtering; - if !found_filtering { - span_lint( - cx, - UNNECESSARY_FILTER_MAP, - expr.span, - "this `.filter_map` can be written more simply using `.map`", - ); - return; - } - - if !found_mapping && !mutates_arg { - span_lint( - cx, - UNNECESSARY_FILTER_MAP, - expr.span, - "this `.filter_map` can be written more simply using `.filter`", - ); + let sugg = if !found_filtering { + "map" + } else if !found_mapping && !mutates_arg { + let in_ty = cx.typeck_results().node_type(body.params[0].hir_id); + match cx.typeck_results().expr_ty(&body.value).kind() { + ty::Adt(adt, subst) + if cx.tcx.is_diagnostic_item(sym::option_type, adt.did) + && TyS::same_type(in_ty, subst.type_at(0)) => + { + "filter" + }, + _ => return, + } + } else { return; - } + }; + span_lint( + cx, + UNNECESSARY_FILTER_MAP, + expr.span, + &format!("this `.filter_map` can be written more simply using `.{}`", sugg), + ); } } diff --git a/tests/ui/unnecessary_filter_map.rs b/tests/ui/unnecessary_filter_map.rs index af858e4abcf55..c58181f518d58 100644 --- a/tests/ui/unnecessary_filter_map.rs +++ b/tests/ui/unnecessary_filter_map.rs @@ -15,3 +15,7 @@ fn main() { let _ = (0..4).filter_map(i32::checked_abs); } + +fn filter_map_none_changes_item_type() -> impl Iterator { + "".chars().filter_map(|_| None) +} From b8046fab238029c32285c30dca6adefb2a5dce24 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Thu, 6 May 2021 14:04:50 +0900 Subject: [PATCH 64/67] Add a missing lint to MSRV config doc --- clippy_lints/src/utils/conf.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index dc079663fc97a..52c1dc3bdd239 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -94,7 +94,7 @@ macro_rules! define_Conf { // N.B., this macro is parsed by util/lintlib.py define_Conf! { - /// Lint: 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. The minimum rust version that the project supports + /// Lint: 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. The minimum rust version that the project supports (msrv: Option = 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 = ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()), From d52316d5a424734f690a29639cf12a6276cff54c Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 6 May 2021 11:32:27 +0200 Subject: [PATCH 65/67] Bump nightly version -> 2021-05-06 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index cd398451783d6..593162f09a788 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-04-22" +channel = "nightly-2021-05-06" components = ["llvm-tools-preview", "rustc-dev", "rust-src"] From d481b38634d35425d87338777c81c19d30b81150 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 6 May 2021 11:32:34 +0200 Subject: [PATCH 66/67] Bump Clippy version -> 0.1.54 --- Cargo.toml | 2 +- clippy_lints/Cargo.toml | 2 +- clippy_utils/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 23a035f9715db..f010e60960491 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.53" +version = "0.1.54" authors = ["The Rust Clippy Developers"] description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 42218c2c00ac5..7ceb1da6a6ebb 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_lints" # begin automatic update -version = "0.1.53" +version = "0.1.54" # end automatic update authors = ["The Rust Clippy Developers"] description = "A bunch of helpful lints to avoid common pitfalls in Rust" diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index 6e158c8ce7239..0a1d4e1114285 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.53" +version = "0.1.54" authors = ["The Rust Clippy Developers"] edition = "2018" publish = false From c24058bc59c074681545be2db454539b9d856582 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 6 May 2021 12:21:46 +0200 Subject: [PATCH 67/67] Update Cargo.lock --- Cargo.lock | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0939f19cdfe20..c98c3526644f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -548,7 +548,7 @@ dependencies = [ [[package]] name = "clippy" -version = "0.1.53" +version = "0.1.54" dependencies = [ "cargo_metadata 0.12.0", "clippy-mini-macro-test", @@ -585,7 +585,7 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.53" +version = "0.1.54" dependencies = [ "cargo_metadata 0.12.0", "clippy_utils", @@ -597,6 +597,7 @@ dependencies = [ "rustc-semver", "semver 0.11.0", "serde", + "serde_json", "toml", "unicode-normalization", "url 2.1.1", @@ -604,7 +605,7 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.53" +version = "0.1.54" dependencies = [ "if_chain", "itertools 0.9.0",