From f0c2ac8a298f7bf828334972cf7c00a20389bbdb Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Thu, 5 May 2022 14:27:11 +0100 Subject: [PATCH 01/19] Bless clippy error msg --- tests/ui/indexing_slicing_index.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/indexing_slicing_index.stderr b/tests/ui/indexing_slicing_index.stderr index 83a36f407d5..6ae700753f0 100644 --- a/tests/ui/indexing_slicing_index.stderr +++ b/tests/ui/indexing_slicing_index.stderr @@ -1,4 +1,4 @@ -error[E0080]: evaluation of `main::{constant#3}::<&i32>` failed +error[E0080]: evaluation of `main::{constant#3}` failed --> $DIR/indexing_slicing_index.rs:31:14 | LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts. From 7cd86aa1be18d608d828239c11e887a762efc92a Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 5 May 2022 15:12:52 +0100 Subject: [PATCH 02/19] Merge commit '7c21f91b15b7604f818565646b686d90f99d1baf' into clippyup --- .github/ISSUE_TEMPLATE/blank_issue.yml | 2 +- .github/ISSUE_TEMPLATE/false_negative.yml | 2 +- .github/ISSUE_TEMPLATE/false_positive.yml | 2 +- .github/workflows/clippy.yml | 6 +- .github/workflows/clippy_bors.yml | 8 +- .github/workflows/clippy_dev.yml | 2 +- .github/workflows/deploy.yml | 4 +- .github/workflows/remark.yml | 2 +- CHANGELOG.md | 113 +++- clippy_dev/Cargo.toml | 1 + clippy_dev/src/lib.rs | 1 + clippy_dev/src/main.rs | 84 ++- clippy_dev/src/new_lint.rs | 6 +- clippy_dev/src/setup/mod.rs | 4 +- clippy_dev/src/update_lints.rs | 405 +++++++++++- clippy_lints/src/attrs.rs | 19 +- clippy_lints/src/await_holding_invalid.rs | 167 +++-- clippy_lints/src/bytes_count_to_len.rs | 70 +++ ...se_sensitive_file_extension_comparisons.rs | 4 +- .../src/casts/cast_possible_truncation.rs | 20 +- .../src/casts/cast_slice_different_sizes.rs | 144 +++-- clippy_lints/src/casts/mod.rs | 3 +- clippy_lints/src/collapsible_if.rs | 18 +- clippy_lints/src/doc.rs | 10 +- clippy_lints/src/drop_forget_ref.rs | 2 +- clippy_lints/src/empty_drop.rs | 65 ++ .../src/empty_structs_with_brackets.rs | 2 +- clippy_lints/src/eta_reduction.rs | 7 +- clippy_lints/src/format_push_string.rs | 77 +++ clippy_lints/src/functions/must_use.rs | 9 +- clippy_lints/src/functions/result_unit_err.rs | 2 +- clippy_lints/src/identity_op.rs | 82 ++- clippy_lints/src/implicit_hasher.rs | 4 +- .../src/inconsistent_struct_constructor.rs | 3 +- clippy_lints/src/index_refutable_slice.rs | 2 +- clippy_lints/src/inherent_impl.rs | 2 +- clippy_lints/src/init_numbered_fields.rs | 2 + clippy_lints/src/large_include_file.rs | 86 +++ clippy_lints/src/lib.register_all.rs | 10 +- clippy_lints/src/lib.register_complexity.rs | 3 +- clippy_lints/src/lib.register_lints.rs | 10 + clippy_lints/src/lib.register_nursery.rs | 3 + clippy_lints/src/lib.register_pedantic.rs | 4 +- clippy_lints/src/lib.register_perf.rs | 2 +- clippy_lints/src/lib.register_restriction.rs | 3 + clippy_lints/src/lib.register_style.rs | 4 + clippy_lints/src/lib.register_suspicious.rs | 1 + clippy_lints/src/lib.rs | 68 +-- clippy_lints/src/lifetimes.rs | 72 ++- clippy_lints/src/literal_representation.rs | 50 +- clippy_lints/src/loops/never_loop.rs | 5 +- clippy_lints/src/manual_bits.rs | 48 +- clippy_lints/src/manual_non_exhaustive.rs | 221 ++++--- clippy_lints/src/map_clone.rs | 12 +- clippy_lints/src/match_result_ok.rs | 4 +- ...h.rs => infallible_destructuring_match.rs} | 0 clippy_lints/src/matches/match_same_arms.rs | 4 +- clippy_lints/src/matches/mod.rs | 9 +- clippy_lints/src/matches/needless_match.rs | 6 +- .../src/matches/redundant_pattern_match.rs | 66 +- .../matches/rest_pat_in_fully_bound_struct.rs | 1 + .../src/methods/is_digit_ascii_radix.rs | 50 ++ clippy_lints/src/methods/iter_with_drain.rs | 95 ++- clippy_lints/src/methods/mod.rs | 111 +++- .../src/methods/needless_option_take.rs | 41 ++ clippy_lints/src/methods/str_splitn.rs | 419 +++++++------ .../src/methods/unnecessary_to_owned.rs | 23 +- .../src/methods/wrong_self_convention.rs | 2 +- clippy_lints/src/misc_early/mod.rs | 2 +- clippy_lints/src/missing_inline.rs | 4 +- clippy_lints/src/needless_bitwise_bool.rs | 4 +- clippy_lints/src/needless_late_init.rs | 54 +- clippy_lints/src/new_without_default.rs | 2 +- clippy_lints/src/non_expressive_names.rs | 2 +- clippy_lints/src/octal_escapes.rs | 2 +- clippy_lints/src/only_used_in_recursion.rs | 56 +- clippy_lints/src/option_if_let_else.rs | 8 +- clippy_lints/src/ptr.rs | 81 +-- clippy_lints/src/pub_use.rs | 56 ++ clippy_lints/src/redundant_pub_crate.rs | 21 +- clippy_lints/src/renamed_lints.rs | 39 ++ clippy_lints/src/same_name_method.rs | 206 +++---- clippy_lints/src/stable_sort_primitive.rs | 20 +- clippy_lints/src/strings.rs | 56 ++ .../src/suspicious_operation_groupings.rs | 2 +- clippy_lints/src/trait_bounds.rs | 15 +- clippy_lints/src/transmute/utils.rs | 20 +- clippy_lints/src/types/mod.rs | 4 +- .../src/undocumented_unsafe_blocks.rs | 7 +- clippy_lints/src/unit_types/let_unit_value.rs | 80 ++- clippy_lints/src/unit_types/mod.rs | 2 +- .../src/unnecessary_owned_empty_strings.rs | 81 +++ clippy_lints/src/unnested_or_patterns.rs | 14 +- clippy_lints/src/use_self.rs | 2 +- clippy_lints/src/utils/author.rs | 2 +- clippy_lints/src/utils/conf.rs | 6 + clippy_lints/src/utils/dump_hir.rs | 55 ++ clippy_lints/src/utils/inspector.rs | 577 ------------------ clippy_lints/src/utils/internal_lints.rs | 4 +- .../internal_lints/metadata_collector.rs | 8 +- clippy_lints/src/utils/mod.rs | 2 +- clippy_utils/src/consts.rs | 18 +- clippy_utils/src/hir_utils.rs | 42 +- clippy_utils/src/macros.rs | 2 +- clippy_utils/src/msrvs.rs | 1 + clippy_utils/src/numeric_literal.rs | 2 +- clippy_utils/src/paths.rs | 3 + clippy_utils/src/qualify_min_const_fn.rs | 5 +- clippy_utils/src/source.rs | 21 +- clippy_utils/src/sugg.rs | 10 +- clippy_utils/src/ty.rs | 71 ++- clippy_utils/src/usage.rs | 33 +- clippy_utils/src/visitors.rs | 68 ++- doc/adding_lints.md | 14 + lintcheck/src/main.rs | 19 +- rust-toolchain | 2 +- src/driver.rs | 3 +- tests/dogfood.rs | 6 +- tests/lint_message_convention.rs | 2 +- .../collapsible_span_lint_calls.stderr | 4 +- .../interning_defined_symbol.fixed | 2 +- tests/ui-internal/interning_defined_symbol.rs | 2 +- .../await_holding_invalid_type.rs | 41 ++ .../await_holding_invalid_type.stderr | 25 + .../await_holding_invalid_type/clippy.toml | 4 + tests/ui-toml/functions_maxlines/test.rs | 1 + tests/ui-toml/functions_maxlines/test.stderr | 8 +- tests/ui-toml/large_include_file/clippy.toml | 1 + .../large_include_file/large_include_file.rs | 16 + .../large_include_file.stderr | 21 + tests/ui-toml/large_include_file/too_big.txt | 1 + .../min_rust_version/min_rust_version.stderr | 2 +- .../toml_unknown_key/conf_unknown_key.stderr | 2 +- tests/ui/assertions_on_constants.rs | 10 +- tests/ui/auxiliary/proc_macro_derive.rs | 2 +- tests/ui/auxiliary/proc_macro_with_span.rs | 32 + tests/ui/bytes_count_to_len.fixed | 34 ++ tests/ui/bytes_count_to_len.rs | 34 ++ tests/ui/bytes_count_to_len.stderr | 28 + tests/ui/cast.rs | 14 +- tests/ui/cast.stderr | 14 +- tests/ui/cast_alignment.rs | 6 +- tests/ui/cast_slice_different_sizes.rs | 43 ++ tests/ui/cast_slice_different_sizes.stderr | 85 ++- tests/ui/collapsible_else_if.fixed | 7 + tests/ui/collapsible_else_if.rs | 9 + tests/ui/collapsible_else_if.stderr | 11 +- tests/ui/crashes/auxiliary/ice-8681-aux.rs | 6 + tests/ui/crashes/ice-2865.rs | 2 +- tests/ui/crashes/ice-3151.rs | 2 +- tests/ui/crashes/ice-5944.rs | 1 + tests/ui/crashes/ice-8250.stderr | 10 +- tests/ui/crashes/ice-8681.rs | 10 + tests/ui/crashes/ice-96721.rs | 10 + tests/ui/crashes/ice-96721.stderr | 8 + tests/ui/default_numeric_fallback_f64.fixed | 15 +- tests/ui/default_numeric_fallback_f64.rs | 15 +- tests/ui/default_numeric_fallback_f64.stderr | 46 +- tests/ui/default_numeric_fallback_i32.fixed | 13 +- tests/ui/default_numeric_fallback_i32.rs | 13 +- tests/ui/default_numeric_fallback_i32.stderr | 50 +- tests/ui/deprecated.rs | 4 + tests/ui/deprecated.stderr | 32 +- tests/ui/doc_unsafe.rs | 2 + tests/ui/doc_unsafe.stderr | 12 +- tests/ui/drop_non_drop.stderr | 4 +- tests/ui/empty_drop.fixed | 24 + tests/ui/empty_drop.rs | 30 + tests/ui/empty_drop.stderr | 22 + tests/ui/eta.fixed | 20 +- tests/ui/eta.rs | 16 + tests/ui/eta.stderr | 14 +- tests/ui/extra_unused_lifetimes.rs | 42 ++ tests/ui/extra_unused_lifetimes.stderr | 20 +- tests/ui/format_push_string.rs | 7 + tests/ui/format_push_string.stderr | 19 + tests/ui/identity_op.rs | 34 +- tests/ui/identity_op.stderr | 128 +++- tests/ui/impl.rs | 2 +- tests/ui/is_digit_ascii_radix.fixed | 18 + tests/ui/is_digit_ascii_radix.rs | 18 + tests/ui/is_digit_ascii_radix.stderr | 22 + tests/ui/iter_overeager_cloned.fixed | 2 +- tests/ui/iter_overeager_cloned.rs | 2 +- tests/ui/iter_with_drain.fixed | 9 + tests/ui/iter_with_drain.rs | 9 + tests/ui/let_underscore_drop.rs | 1 + tests/ui/let_underscore_drop.stderr | 6 +- tests/ui/let_unit.fixed | 52 ++ tests/ui/let_unit.rs | 52 ++ tests/ui/let_unit.stderr | 71 ++- tests/ui/manual_bits.fixed | 63 +- tests/ui/manual_bits.rs | 13 +- tests/ui/manual_bits.stderr | 170 +++--- tests/ui/manual_non_exhaustive_enum.rs | 78 +++ tests/ui/manual_non_exhaustive_enum.stderr | 41 ++ ...ive.rs => manual_non_exhaustive_struct.rs} | 63 -- ...rr => manual_non_exhaustive_struct.stderr} | 58 +- tests/ui/manual_split_once.fixed | 117 +++- tests/ui/manual_split_once.rs | 117 +++- tests/ui/manual_split_once.stderr | 221 +++++-- tests/ui/mistyped_literal_suffix.fixed | 16 +- tests/ui/mistyped_literal_suffix.rs | 16 +- tests/ui/mistyped_literal_suffix.stderr | 54 +- tests/ui/mut_from_ref.rs | 20 +- tests/ui/mut_from_ref.stderr | 14 +- tests/ui/needless_for_each_fixable.fixed | 7 +- tests/ui/needless_for_each_fixable.rs | 7 +- tests/ui/needless_for_each_fixable.stderr | 16 +- tests/ui/needless_late_init.rs | 73 ++- tests/ui/needless_late_init.stderr | 99 +-- tests/ui/needless_late_init_fixable.fixed | 8 +- tests/ui/needless_late_init_fixable.rs | 10 +- tests/ui/needless_late_init_fixable.stderr | 49 +- tests/ui/needless_match.fixed | 12 + tests/ui/needless_match.rs | 12 + tests/ui/needless_match.stderr | 8 +- tests/ui/needless_option_as_deref.fixed | 14 + tests/ui/needless_option_as_deref.rs | 14 + tests/ui/needless_option_take.fixed | 15 + tests/ui/needless_option_take.rs | 15 + tests/ui/needless_option_take.stderr | 10 + tests/ui/needless_splitn.fixed | 20 + tests/ui/needless_splitn.rs | 20 + tests/ui/needless_splitn.stderr | 44 +- tests/ui/new_without_default.rs | 2 +- tests/ui/non_expressive_names.rs | 2 +- tests/ui/numbered_fields.fixed | 5 + tests/ui/numbered_fields.rs | 5 + tests/ui/option_if_let_else.fixed | 7 +- tests/ui/option_if_let_else.rs | 7 +- tests/ui/option_if_let_else.stderr | 30 +- tests/ui/option_take_on_temporary.fixed | 15 + tests/ui/or_then_unwrap.fixed | 2 +- tests/ui/or_then_unwrap.rs | 2 +- tests/ui/panicking_macros.rs | 2 +- tests/ui/pub_use.rs | 14 + tests/ui/pub_use.stderr | 11 + tests/ui/redundant_pub_crate.fixed | 10 + tests/ui/redundant_pub_crate.rs | 10 + tests/ui/rename.fixed | 93 ++- tests/ui/rename.rs | 93 ++- tests/ui/rename.stderr | 270 ++++---- tests/ui/rest_pat_in_fully_bound_structs.rs | 15 + tests/ui/same_functions_in_if_condition.rs | 19 + .../ui/same_functions_in_if_condition.stderr | 24 +- tests/ui/shadow.rs | 1 + tests/ui/shadow.stderr | 88 +-- tests/ui/similar_names.rs | 3 +- tests/ui/similar_names.stderr | 28 +- tests/ui/single_char_lifetime_names.rs | 1 + tests/ui/single_char_lifetime_names.stderr | 10 +- tests/ui/single_match_else.rs | 18 +- tests/ui/single_match_else.stderr | 11 +- tests/ui/stable_sort_primitive.stderr | 14 +- tests/ui/suspicious_else_formatting.rs | 2 +- tests/ui/to_digit_is_some.fixed | 4 +- tests/ui/to_digit_is_some.rs | 4 +- tests/ui/to_digit_is_some.stderr | 8 +- tests/ui/trait_duplication_in_bounds.rs | 3 + tests/ui/trait_duplication_in_bounds.stderr | 10 +- tests/ui/transmute_collection.rs | 5 +- tests/ui/trim_split_whitespace.fixed | 91 +++ tests/ui/trim_split_whitespace.rs | 91 +++ tests/ui/trim_split_whitespace.stderr | 52 ++ tests/ui/type_repetition_in_bounds.rs | 3 + tests/ui/type_repetition_in_bounds.stderr | 10 +- tests/ui/undocumented_unsafe_blocks.rs | 1 + tests/ui/undocumented_unsafe_blocks.stderr | 36 +- tests/ui/uninit.rs | 1 + tests/ui/uninit.stderr | 6 +- tests/ui/unit_arg.rs | 3 +- tests/ui/unit_arg.stderr | 20 +- tests/ui/unit_hash.rs | 1 + tests/ui/unit_hash.stderr | 6 +- .../ui/unnecessary_owned_empty_strings.fixed | 22 + tests/ui/unnecessary_owned_empty_strings.rs | 22 + .../ui/unnecessary_owned_empty_strings.stderr | 16 + tests/ui/unnecessary_to_owned.fixed | 12 + tests/ui/unnecessary_to_owned.rs | 12 + tests/ui/unnecessary_to_owned.stderr | 196 +++--- tests/ui/unnested_or_patterns.fixed | 7 +- tests/ui/unnested_or_patterns.rs | 7 +- tests/ui/unnested_or_patterns.stderr | 40 +- tests/ui/useless_attribute.fixed | 6 + tests/ui/useless_attribute.rs | 6 + tests/ui/useless_attribute.stderr | 2 +- tests/ui/wildcard_imports.fixed | 3 +- tests/ui/wildcard_imports.rs | 3 +- tests/ui/wildcard_imports.stderr | 42 +- tests/ui/wrong_self_convention.rs | 5 - tests/ui/wrong_self_convention.stderr | 18 +- tests/ui/wrong_self_convention2.rs | 10 + 293 files changed, 6318 insertions(+), 2753 deletions(-) create mode 100644 clippy_lints/src/bytes_count_to_len.rs create mode 100644 clippy_lints/src/empty_drop.rs create mode 100644 clippy_lints/src/format_push_string.rs create mode 100644 clippy_lints/src/large_include_file.rs rename clippy_lints/src/matches/{infalliable_detructuring_match.rs => infallible_destructuring_match.rs} (100%) create mode 100644 clippy_lints/src/methods/is_digit_ascii_radix.rs create mode 100644 clippy_lints/src/methods/needless_option_take.rs create mode 100644 clippy_lints/src/pub_use.rs create mode 100644 clippy_lints/src/renamed_lints.rs create mode 100644 clippy_lints/src/unnecessary_owned_empty_strings.rs create mode 100644 clippy_lints/src/utils/dump_hir.rs delete mode 100644 clippy_lints/src/utils/inspector.rs create mode 100644 tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs create mode 100644 tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr create mode 100644 tests/ui-toml/await_holding_invalid_type/clippy.toml create mode 100644 tests/ui-toml/large_include_file/clippy.toml create mode 100644 tests/ui-toml/large_include_file/large_include_file.rs create mode 100644 tests/ui-toml/large_include_file/large_include_file.stderr create mode 100644 tests/ui-toml/large_include_file/too_big.txt create mode 100644 tests/ui/auxiliary/proc_macro_with_span.rs create mode 100644 tests/ui/bytes_count_to_len.fixed create mode 100644 tests/ui/bytes_count_to_len.rs create mode 100644 tests/ui/bytes_count_to_len.stderr create mode 100644 tests/ui/crashes/auxiliary/ice-8681-aux.rs create mode 100644 tests/ui/crashes/ice-8681.rs create mode 100644 tests/ui/crashes/ice-96721.rs create mode 100644 tests/ui/crashes/ice-96721.stderr create mode 100644 tests/ui/empty_drop.fixed create mode 100644 tests/ui/empty_drop.rs create mode 100644 tests/ui/empty_drop.stderr create mode 100644 tests/ui/format_push_string.rs create mode 100644 tests/ui/format_push_string.stderr create mode 100644 tests/ui/is_digit_ascii_radix.fixed create mode 100644 tests/ui/is_digit_ascii_radix.rs create mode 100644 tests/ui/is_digit_ascii_radix.stderr create mode 100644 tests/ui/manual_non_exhaustive_enum.rs create mode 100644 tests/ui/manual_non_exhaustive_enum.stderr rename tests/ui/{manual_non_exhaustive.rs => manual_non_exhaustive_struct.rs} (58%) rename tests/ui/{manual_non_exhaustive.stderr => manual_non_exhaustive_struct.stderr} (53%) create mode 100644 tests/ui/needless_option_take.fixed create mode 100644 tests/ui/needless_option_take.rs create mode 100644 tests/ui/needless_option_take.stderr create mode 100644 tests/ui/option_take_on_temporary.fixed create mode 100644 tests/ui/pub_use.rs create mode 100644 tests/ui/pub_use.stderr create mode 100644 tests/ui/trim_split_whitespace.fixed create mode 100644 tests/ui/trim_split_whitespace.rs create mode 100644 tests/ui/trim_split_whitespace.stderr create mode 100644 tests/ui/unnecessary_owned_empty_strings.fixed create mode 100644 tests/ui/unnecessary_owned_empty_strings.rs create mode 100644 tests/ui/unnecessary_owned_empty_strings.stderr diff --git a/.github/ISSUE_TEMPLATE/blank_issue.yml b/.github/ISSUE_TEMPLATE/blank_issue.yml index d610e8c7bc4..89884bfc859 100644 --- a/.github/ISSUE_TEMPLATE/blank_issue.yml +++ b/.github/ISSUE_TEMPLATE/blank_issue.yml @@ -9,7 +9,7 @@ body: attributes: label: Description description: > - Please provide a discription of the issue, along with any information + Please provide a description of the issue, along with any information you feel relevant to replicate it. validations: required: true diff --git a/.github/ISSUE_TEMPLATE/false_negative.yml b/.github/ISSUE_TEMPLATE/false_negative.yml index 9357ccc4f4e..25e436d30b9 100644 --- a/.github/ISSUE_TEMPLATE/false_negative.yml +++ b/.github/ISSUE_TEMPLATE/false_negative.yml @@ -23,7 +23,7 @@ body: id: reproducer attributes: label: Reproducer - description: Please provide the code and steps to repoduce the bug + description: Please provide the code and steps to reproduce the bug value: | I tried this code: diff --git a/.github/ISSUE_TEMPLATE/false_positive.yml b/.github/ISSUE_TEMPLATE/false_positive.yml index b7dd400ee73..561b65c93a7 100644 --- a/.github/ISSUE_TEMPLATE/false_positive.yml +++ b/.github/ISSUE_TEMPLATE/false_positive.yml @@ -24,7 +24,7 @@ body: attributes: label: Reproducer description: > - Please provide the code and steps to repoduce the bug together with the + Please provide the code and steps to reproduce the bug together with the output from Clippy. value: | I tried this code: diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index cd83bc9642b..0e27cc927ac 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -6,14 +6,14 @@ on: branches-ignore: - auto - try - # Don't run Clippy tests, when only textfiles were modified + # Don't run Clippy tests, when only text files were modified paths-ignore: - 'COPYRIGHT' - 'LICENSE-*' - '**.md' - '**.txt' pull_request: - # Don't run Clippy tests, when only textfiles were modified + # Don't run Clippy tests, when only text files were modified paths-ignore: - 'COPYRIGHT' - 'LICENSE-*' @@ -37,7 +37,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 - name: Install toolchain run: rustup show active-toolchain diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index f571485e6d3..9b3fd3ddfeb 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -25,7 +25,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 with: ref: ${{ github.ref }} @@ -88,7 +88,7 @@ jobs: if: matrix.host == 'i686-unknown-linux-gnu' - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 - name: Install toolchain run: rustup show active-toolchain @@ -154,7 +154,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 - name: Install toolchain run: rustup show active-toolchain @@ -212,7 +212,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 - name: Install toolchain run: rustup show active-toolchain diff --git a/.github/workflows/clippy_dev.yml b/.github/workflows/clippy_dev.yml index 5dfab1d2ebc..22051093c9c 100644 --- a/.github/workflows/clippy_dev.yml +++ b/.github/workflows/clippy_dev.yml @@ -23,7 +23,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 # Run - name: Build diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index b8be730be32..71d71d10359 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -21,10 +21,10 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 with: ref: ${{ env.TARGET_BRANCH }} path: 'out' diff --git a/.github/workflows/remark.yml b/.github/workflows/remark.yml index 56c00544c93..a179bfa7261 100644 --- a/.github/workflows/remark.yml +++ b/.github/workflows/remark.yml @@ -16,7 +16,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 - name: Setup Node.js uses: actions/setup-node@v1.4.4 diff --git a/CHANGELOG.md b/CHANGELOG.md index b4097ea86a5..751f9fccd88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,108 @@ document. ## Unreleased / In Rust Nightly -[57b3c4b...master](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...master) +[d0cf3481...master](https://github.com/rust-lang/rust-clippy/compare/d0cf3481...master) + +## Rust 1.61 (beta) + +Current beta, released 2022-05-19 + +[57b3c4b...d0cf3481](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...d0cf3481) + +### New Lints + +* [`only_used_in_recursion`] + [#8422](https://github.com/rust-lang/rust-clippy/pull/8422) +* [`cast_enum_truncation`] + [#8381](https://github.com/rust-lang/rust-clippy/pull/8381) +* [`missing_spin_loop`] + [#8174](https://github.com/rust-lang/rust-clippy/pull/8174) +* [`deref_by_slicing`] + [#8218](https://github.com/rust-lang/rust-clippy/pull/8218) +* [`needless_match`] + [#8471](https://github.com/rust-lang/rust-clippy/pull/8471) +* [`allow_attributes_without_reason`] (Requires `#![feature(lint_reasons)]`) + [#8504](https://github.com/rust-lang/rust-clippy/pull/8504) +* [`print_in_format_impl`] + [#8253](https://github.com/rust-lang/rust-clippy/pull/8253) +* [`unnecessary_find_map`] + [#8489](https://github.com/rust-lang/rust-clippy/pull/8489) +* [`or_then_unwrap`] + [#8561](https://github.com/rust-lang/rust-clippy/pull/8561) +* [`unnecessary_join`] + [#8579](https://github.com/rust-lang/rust-clippy/pull/8579) +* [`iter_with_drain`] + [#8483](https://github.com/rust-lang/rust-clippy/pull/8483) +* [`cast_enum_constructor`] + [#8562](https://github.com/rust-lang/rust-clippy/pull/8562) +* [`cast_slice_different_sizes`] + [#8445](https://github.com/rust-lang/rust-clippy/pull/8445) + +### Moves and Deprecations + +* Moved [`transmute_undefined_repr`] to `nursery` (now allow-by-default) + [#8432](https://github.com/rust-lang/rust-clippy/pull/8432) +* Moved [`try_err`] to `restriction` + [#8544](https://github.com/rust-lang/rust-clippy/pull/8544) +* Move [`iter_with_drain`] to `nursery` + [#8541](https://github.com/rust-lang/rust-clippy/pull/8541) +* Renamed `to_string_in_display` to [`recursive_format_impl`] + [#8188](https://github.com/rust-lang/rust-clippy/pull/8188) + +### Enhancements + +* [`dbg_macro`]: The lint level can now be set with crate attributes and works inside macros + [#8411](https://github.com/rust-lang/rust-clippy/pull/8411) +* [`ptr_as_ptr`]: Now works inside macros + [#8442](https://github.com/rust-lang/rust-clippy/pull/8442) +* [`use_self`]: Now works for variants in match expressions + [#8456](https://github.com/rust-lang/rust-clippy/pull/8456) +* [`await_holding_lock`]: Now lints for `parking_lot::{Mutex, RwLock}` + [#8419](https://github.com/rust-lang/rust-clippy/pull/8419) +* [`recursive_format_impl`]: Now checks for format calls on `self` + [#8188](https://github.com/rust-lang/rust-clippy/pull/8188) + +### False Positive Fixes + +* [`new_without_default`]: No longer lints for `new()` methods with `#[doc(hidden)]` + [#8472](https://github.com/rust-lang/rust-clippy/pull/8472) +* [`transmute_undefined_repr`]: No longer lints for single field structs with `#[repr(C)]`, + generic parameters, wide pointers, unions, tuples and allow several forms of type erasure + [#8425](https://github.com/rust-lang/rust-clippy/pull/8425) + [#8553](https://github.com/rust-lang/rust-clippy/pull/8553) + [#8440](https://github.com/rust-lang/rust-clippy/pull/8440) + [#8547](https://github.com/rust-lang/rust-clippy/pull/8547) +* [`match_single_binding`], [`match_same_arms`], [`match_as_ref`], [`match_bool`]: No longer + lint `match` expressions with `cfg`ed arms + [#8443](https://github.com/rust-lang/rust-clippy/pull/8443) +* [`single_component_path_imports`]: No longer lint on macros + [#8537](https://github.com/rust-lang/rust-clippy/pull/8537) +* [`ptr_arg`]: Allow `&mut` arguments for `Cow<_>` + [#8552](https://github.com/rust-lang/rust-clippy/pull/8552) +* [`needless_borrow`]: No longer lints for method calls + [#8441](https://github.com/rust-lang/rust-clippy/pull/8441) +* [`match_same_arms`]: Now ensures that interposing arm patterns don't overlap + [#8232](https://github.com/rust-lang/rust-clippy/pull/8232) +* [`default_trait_access`]: Now allows `Default::default` in update expressions + [#8433](https://github.com/rust-lang/rust-clippy/pull/8433) + +### Suggestion Fixes/Improvements + +* [`redundant_slicing`]: Fixed suggestion for a method calls + [#8218](https://github.com/rust-lang/rust-clippy/pull/8218) +* [`map_flatten`]: Long suggestions will now be split up into two help messages + [#8520](https://github.com/rust-lang/rust-clippy/pull/8520) +* [`unnecessary_lazy_evaluations`]: Now shows suggestions for longer code snippets + [#8543](https://github.com/rust-lang/rust-clippy/pull/8543) +* [`unnecessary_sort_by`]: Now suggests `Reverse` including the path + [#8462](https://github.com/rust-lang/rust-clippy/pull/8462) +* [`search_is_some`]: More suggestions are now `MachineApplicable` + [#8536](https://github.com/rust-lang/rust-clippy/pull/8536) + +### Documentation Improvements + +* [`new_without_default`]: Document `pub` requirement for the struct and fields + [#8429](https://github.com/rust-lang/rust-clippy/pull/8429) ## Rust 1.60 @@ -3182,6 +3283,7 @@ Released 2018-09-13 [`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern [`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops [`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async +[`await_holding_invalid_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_invalid_type [`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock [`await_holding_refcell_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_refcell_ref [`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask @@ -3198,6 +3300,7 @@ Released 2018-09-13 [`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local [`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code [`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow +[`bytes_count_to_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_count_to_len [`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth [`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata [`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons @@ -3262,6 +3365,7 @@ Released 2018-09-13 [`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument [`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec [`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else +[`empty_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_drop [`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum [`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr [`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop @@ -3314,6 +3418,7 @@ Released 2018-09-13 [`forget_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_non_drop [`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref [`format_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_in_format_args +[`format_push_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_push_string [`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect [`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into [`from_str_radix_10`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_str_radix_10 @@ -3356,6 +3461,7 @@ Released 2018-09-13 [`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 +[`is_digit_ascii_radix`]: https://rust-lang.github.io/rust-clippy/master/index.html#is_digit_ascii_radix [`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements [`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect [`iter_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_count @@ -3372,6 +3478,7 @@ Released 2018-09-13 [`large_const_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays [`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups [`large_enum_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant +[`large_include_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_include_file [`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays [`large_types_passed_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value [`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty @@ -3472,6 +3579,7 @@ Released 2018-09-13 [`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes [`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match [`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref +[`needless_option_take`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_take [`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value [`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark [`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop @@ -3527,6 +3635,7 @@ Released 2018-09-13 [`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq [`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast [`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names +[`pub_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_use [`question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark [`range_minus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_minus_one [`range_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one @@ -3627,6 +3736,7 @@ Released 2018-09-13 [`transmute_undefined_repr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_undefined_repr [`transmutes_expressible_as_ptr_casts`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmutes_expressible_as_ptr_casts [`transmuting_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmuting_null +[`trim_split_whitespace`]: https://rust-lang.github.io/rust-clippy/master/index.html#trim_split_whitespace [`trivial_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivial_regex [`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref [`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err @@ -3650,6 +3760,7 @@ Released 2018-09-13 [`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_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings [`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_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml index 81faa5fe5e1..2cfbcea5034 100644 --- a/clippy_dev/Cargo.toml +++ b/clippy_dev/Cargo.toml @@ -4,6 +4,7 @@ version = "0.0.1" edition = "2021" [dependencies] +aho-corasick = "0.7" clap = "2.33" indoc = "1.0" itertools = "0.10.1" diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 9c6d754b400..81e807cf10c 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -1,3 +1,4 @@ +#![feature(let_chains)] #![feature(let_else)] #![feature(once_cell)] #![feature(rustc_private)] diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index b1fe35a0243..ebf8f38d490 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -19,9 +19,9 @@ fn main() { if matches.is_present("print-only") { update_lints::print_lints(); } else if matches.is_present("check") { - update_lints::run(update_lints::UpdateMode::Check); + update_lints::update(update_lints::UpdateMode::Check); } else { - update_lints::run(update_lints::UpdateMode::Change); + update_lints::update(update_lints::UpdateMode::Change); } }, ("new_lint", Some(matches)) => { @@ -31,18 +31,36 @@ fn main() { matches.value_of("category"), matches.is_present("msrv"), ) { - Ok(_) => update_lints::run(update_lints::UpdateMode::Change), + Ok(_) => update_lints::update(update_lints::UpdateMode::Change), Err(e) => eprintln!("Unable to create lint: {}", e), } }, ("setup", Some(sub_command)) => match sub_command.subcommand() { - ("intellij", Some(matches)) => setup::intellij::setup_rustc_src( - matches - .value_of("rustc-repo-path") - .expect("this field is mandatory and therefore always valid"), - ), - ("git-hook", Some(matches)) => setup::git_hook::install_hook(matches.is_present("force-override")), - ("vscode-tasks", Some(matches)) => setup::vscode::install_tasks(matches.is_present("force-override")), + ("intellij", Some(matches)) => { + if matches.is_present("remove") { + setup::intellij::remove_rustc_src(); + } else { + setup::intellij::setup_rustc_src( + matches + .value_of("rustc-repo-path") + .expect("this field is mandatory and therefore always valid"), + ); + } + }, + ("git-hook", Some(matches)) => { + if matches.is_present("remove") { + setup::git_hook::remove_hook(); + } else { + setup::git_hook::install_hook(matches.is_present("force-override")); + } + }, + ("vscode-tasks", Some(matches)) => { + if matches.is_present("remove") { + setup::vscode::remove_tasks(); + } else { + setup::vscode::install_tasks(matches.is_present("force-override")); + } + }, _ => {}, }, ("remove", Some(sub_command)) => match sub_command.subcommand() { @@ -60,6 +78,12 @@ fn main() { let path = matches.value_of("path").unwrap(); lint::run(path); }, + ("rename_lint", Some(matches)) => { + let old_name = matches.value_of("old_name").unwrap(); + let new_name = matches.value_of("new_name").unwrap_or(old_name); + let uplift = matches.is_present("uplift"); + update_lints::rename(old_name, new_name, uplift); + }, _ => {}, } } @@ -167,6 +191,12 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { .subcommand( SubCommand::with_name("intellij") .about("Alter dependencies so Intellij Rust can find rustc internals") + .arg( + Arg::with_name("remove") + .long("remove") + .help("Remove the dependencies added with 'cargo dev setup intellij'") + .required(false), + ) .arg( Arg::with_name("rustc-repo-path") .long("repo-path") @@ -174,12 +204,19 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { .help("The path to a rustc repo that will be used for setting the dependencies") .takes_value(true) .value_name("path") + .conflicts_with("remove") .required(true), ), ) .subcommand( SubCommand::with_name("git-hook") .about("Add a pre-commit git hook that formats your code to make it look pretty") + .arg( + Arg::with_name("remove") + .long("remove") + .help("Remove the pre-commit hook added with 'cargo dev setup git-hook'") + .required(false), + ) .arg( Arg::with_name("force-override") .long("force-override") @@ -191,6 +228,12 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { .subcommand( SubCommand::with_name("vscode-tasks") .about("Add several tasks to vscode for formatting, validation and testing") + .arg( + Arg::with_name("remove") + .long("remove") + .help("Remove the tasks added with 'cargo dev setup vscode-tasks'") + .required(false), + ) .arg( Arg::with_name("force-override") .long("force-override") @@ -242,5 +285,26 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { .help("The path to a file or package directory to lint"), ), ) + .subcommand( + SubCommand::with_name("rename_lint") + .about("Renames the given lint") + .arg( + Arg::with_name("old_name") + .index(1) + .required(true) + .help("The name of the lint to rename"), + ) + .arg( + Arg::with_name("new_name") + .index(2) + .required_unless("uplift") + .help("The new name of the lint"), + ) + .arg( + Arg::with_name("uplift") + .long("uplift") + .help("This lint will be uplifted into rustc"), + ), + ) .get_matches() } diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 7a3fd131761..10f67d301f8 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -1,5 +1,6 @@ use crate::clippy_project_root; use indoc::indoc; +use std::fmt::Write as _; use std::fs::{self, OpenOptions}; use std::io::prelude::*; use std::io::{self, ErrorKind}; @@ -232,7 +233,8 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { ) }); - result.push_str(&format!( + let _ = write!( + result, indoc! {r#" declare_clippy_lint! {{ /// ### What it does @@ -256,7 +258,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { version = version, name_upper = name_upper, category = category, - )); + ); result.push_str(&if enable_msrv { format!( diff --git a/clippy_dev/src/setup/mod.rs b/clippy_dev/src/setup/mod.rs index a1e4dd103b8..f691ae4fa45 100644 --- a/clippy_dev/src/setup/mod.rs +++ b/clippy_dev/src/setup/mod.rs @@ -7,7 +7,7 @@ use std::path::Path; const CLIPPY_DEV_DIR: &str = "clippy_dev"; /// This function verifies that the tool is being executed in the clippy directory. -/// This is useful to ensure that setups only modify Clippys resources. The verification +/// This is useful to ensure that setups only modify Clippy's resources. The verification /// is done by checking that `clippy_dev` is a sub directory of the current directory. /// /// It will print an error message and return `false` if the directory could not be @@ -17,7 +17,7 @@ fn verify_inside_clippy_dir() -> bool { if path.exists() && path.is_dir() { true } else { - eprintln!("error: unable to verify that the working directory is clippys directory"); + eprintln!("error: unable to verify that the working directory is clippy's directory"); false } } diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 59db51fbfac..1a6a4336da2 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -1,11 +1,13 @@ -use core::fmt::Write; +use aho_corasick::AhoCorasickBuilder; +use core::fmt::Write as _; use itertools::Itertools; use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind}; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::ffi::OsStr; use std::fs; -use std::path::Path; -use walkdir::WalkDir; +use std::io::{self, Read as _, Seek as _, Write as _}; +use std::path::{Path, PathBuf}; +use walkdir::{DirEntry, WalkDir}; use crate::clippy_project_root; @@ -30,12 +32,19 @@ pub enum UpdateMode { /// # Panics /// /// Panics if a file path could not read from or then written to -#[allow(clippy::too_many_lines)] -pub fn run(update_mode: UpdateMode) { - let (lints, deprecated_lints) = gather_all(); +pub fn update(update_mode: UpdateMode) { + let (lints, deprecated_lints, renamed_lints) = gather_all(); + generate_lint_files(update_mode, &lints, &deprecated_lints, &renamed_lints); +} - let internal_lints = Lint::internal_lints(&lints); - let usable_lints = Lint::usable_lints(&lints); +fn generate_lint_files( + update_mode: UpdateMode, + lints: &[Lint], + deprecated_lints: &[DeprecatedLint], + renamed_lints: &[RenamedLint], +) { + let internal_lints = Lint::internal_lints(lints); + let usable_lints = Lint::usable_lints(lints); let mut sorted_usable_lints = usable_lints.clone(); sorted_usable_lints.sort_by_key(|lint| lint.name.clone()); @@ -87,7 +96,7 @@ pub fn run(update_mode: UpdateMode) { process_file( "clippy_lints/src/lib.deprecated.rs", update_mode, - &gen_deprecated(&deprecated_lints), + &gen_deprecated(deprecated_lints), ); let all_group_lints = usable_lints.iter().filter(|l| { @@ -107,10 +116,16 @@ pub fn run(update_mode: UpdateMode) { &content, ); } + + let content = gen_deprecated_lints_test(deprecated_lints); + process_file("tests/ui/deprecated.rs", update_mode, &content); + + let content = gen_renamed_lints_test(renamed_lints); + process_file("tests/ui/rename.rs", update_mode, &content); } pub fn print_lints() { - let (lint_list, _) = gather_all(); + let (lint_list, _, _) = gather_all(); let usable_lints = Lint::usable_lints(&lint_list); let usable_lint_count = usable_lints.len(); let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter()); @@ -128,6 +143,209 @@ pub fn print_lints() { println!("there are {} lints", usable_lint_count); } +/// Runs the `rename_lint` command. +/// +/// This does the following: +/// * Adds an entry to `renamed_lints.rs`. +/// * Renames all lint attributes to the new name (e.g. `#[allow(clippy::lint_name)]`). +/// * Renames the lint struct to the new name. +/// * Renames the module containing the lint struct to the new name if it shares a name with the +/// lint. +/// +/// # Panics +/// Panics for the following conditions: +/// * If a file path could not read from or then written to +/// * If either lint name has a prefix +/// * If `old_name` doesn't name an existing lint. +/// * If `old_name` names a deprecated or renamed lint. +#[allow(clippy::too_many_lines)] +pub fn rename(old_name: &str, new_name: &str, uplift: bool) { + if let Some((prefix, _)) = old_name.split_once("::") { + panic!("`{}` should not contain the `{}` prefix", old_name, prefix); + } + if let Some((prefix, _)) = new_name.split_once("::") { + panic!("`{}` should not contain the `{}` prefix", new_name, prefix); + } + + let (mut lints, deprecated_lints, mut renamed_lints) = gather_all(); + let mut old_lint_index = None; + let mut found_new_name = false; + for (i, lint) in lints.iter().enumerate() { + if lint.name == old_name { + old_lint_index = Some(i); + } else if lint.name == new_name { + found_new_name = true; + } + } + let old_lint_index = old_lint_index.unwrap_or_else(|| panic!("could not find lint `{}`", old_name)); + + let lint = RenamedLint { + old_name: format!("clippy::{}", old_name), + new_name: if uplift { + new_name.into() + } else { + format!("clippy::{}", new_name) + }, + }; + + // Renamed lints and deprecated lints shouldn't have been found in the lint list, but check just in + // case. + assert!( + !renamed_lints.iter().any(|l| lint.old_name == l.old_name), + "`{}` has already been renamed", + old_name + ); + assert!( + !deprecated_lints.iter().any(|l| lint.old_name == l.name), + "`{}` has already been deprecated", + old_name + ); + + // Update all lint level attributes. (`clippy::lint_name`) + for file in WalkDir::new(clippy_project_root()) + .into_iter() + .map(Result::unwrap) + .filter(|f| { + let name = f.path().file_name(); + let ext = f.path().extension(); + (ext == Some(OsStr::new("rs")) || ext == Some(OsStr::new("fixed"))) + && name != Some(OsStr::new("rename.rs")) + && name != Some(OsStr::new("renamed_lints.rs")) + }) + { + rewrite_file(file.path(), |s| { + replace_ident_like(s, &[(&lint.old_name, &lint.new_name)]) + }); + } + + renamed_lints.push(lint); + renamed_lints.sort_by(|lhs, rhs| { + lhs.new_name + .starts_with("clippy::") + .cmp(&rhs.new_name.starts_with("clippy::")) + .reverse() + .then_with(|| lhs.old_name.cmp(&rhs.old_name)) + }); + + write_file( + Path::new("clippy_lints/src/renamed_lints.rs"), + &gen_renamed_lints_list(&renamed_lints), + ); + + if uplift { + write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints)); + println!( + "`{}` has be uplifted. All the code inside `clippy_lints` related to it needs to be removed manually.", + old_name + ); + } else if found_new_name { + write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints)); + println!( + "`{}` is already defined. The old linting code inside `clippy_lints` needs to be updated/removed manually.", + new_name + ); + } else { + // Rename the lint struct and source files sharing a name with the lint. + let lint = &mut lints[old_lint_index]; + let old_name_upper = old_name.to_uppercase(); + let new_name_upper = new_name.to_uppercase(); + lint.name = new_name.into(); + + // Rename test files. only rename `.stderr` and `.fixed` files if the new test name doesn't exist. + if try_rename_file( + Path::new(&format!("tests/ui/{}.rs", old_name)), + Path::new(&format!("tests/ui/{}.rs", new_name)), + ) { + try_rename_file( + Path::new(&format!("tests/ui/{}.stderr", old_name)), + Path::new(&format!("tests/ui/{}.stderr", new_name)), + ); + try_rename_file( + Path::new(&format!("tests/ui/{}.fixed", old_name)), + Path::new(&format!("tests/ui/{}.fixed", new_name)), + ); + } + + // Try to rename the file containing the lint if the file name matches the lint's name. + let replacements; + let replacements = if lint.module == old_name + && try_rename_file( + Path::new(&format!("clippy_lints/src/{}.rs", old_name)), + Path::new(&format!("clippy_lints/src/{}.rs", new_name)), + ) { + // Edit the module name in the lint list. Note there could be multiple lints. + for lint in lints.iter_mut().filter(|l| l.module == old_name) { + lint.module = new_name.into(); + } + replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)]; + replacements.as_slice() + } else if !lint.module.contains("::") + // Catch cases like `methods/lint_name.rs` where the lint is stored in `methods/mod.rs` + && try_rename_file( + Path::new(&format!("clippy_lints/src/{}/{}.rs", lint.module, old_name)), + Path::new(&format!("clippy_lints/src/{}/{}.rs", lint.module, new_name)), + ) + { + // Edit the module name in the lint list. Note there could be multiple lints, or none. + let renamed_mod = format!("{}::{}", lint.module, old_name); + for lint in lints.iter_mut().filter(|l| l.module == renamed_mod) { + lint.module = format!("{}::{}", lint.module, new_name); + } + replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)]; + replacements.as_slice() + } else { + replacements = [(&*old_name_upper, &*new_name_upper), ("", "")]; + &replacements[0..1] + }; + + // Don't change `clippy_utils/src/renamed_lints.rs` here as it would try to edit the lint being + // renamed. + for (_, file) in clippy_lints_src_files().filter(|(rel_path, _)| rel_path != OsStr::new("renamed_lints.rs")) { + rewrite_file(file.path(), |s| replace_ident_like(s, replacements)); + } + + generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); + println!("{} has been successfully renamed", old_name); + } + + println!("note: `cargo uitest` still needs to be run to update the test results"); +} + +/// Replace substrings if they aren't bordered by identifier characters. Returns `None` if there +/// were no replacements. +fn replace_ident_like(contents: &str, replacements: &[(&str, &str)]) -> Option { + fn is_ident_char(c: u8) -> bool { + matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_') + } + + let searcher = AhoCorasickBuilder::new() + .dfa(true) + .match_kind(aho_corasick::MatchKind::LeftmostLongest) + .build_with_size::(replacements.iter().map(|&(x, _)| x.as_bytes())) + .unwrap(); + + let mut result = String::with_capacity(contents.len() + 1024); + let mut pos = 0; + let mut edited = false; + for m in searcher.find_iter(contents) { + let (old, new) = replacements[m.pattern()]; + result.push_str(&contents[pos..m.start()]); + result.push_str( + if !is_ident_char(contents.as_bytes().get(m.start().wrapping_sub(1)).copied().unwrap_or(0)) + && !is_ident_char(contents.as_bytes().get(m.end()).copied().unwrap_or(0)) + { + edited = true; + new + } else { + old + }, + ); + pos = m.end(); + } + result.push_str(&contents[pos..]); + edited.then(|| result) +} + fn round_to_fifty(count: usize) -> usize { count / 50 * 50 } @@ -210,6 +428,19 @@ impl DeprecatedLint { } } +struct RenamedLint { + old_name: String, + new_name: String, +} +impl RenamedLint { + fn new(old_name: &str, new_name: &str) -> Self { + Self { + old_name: remove_line_splices(old_name), + new_name: remove_line_splices(new_name), + } + } +} + /// Generates the code for registering a group fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator) -> String { let mut details: Vec<_> = lints.map(|l| (&l.module, l.name.to_uppercase())).collect(); @@ -217,12 +448,13 @@ fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator String { let mut output = GENERATED_FILE_COMMENT.to_string(); output.push_str("{\n"); for lint in lints { - output.push_str(&format!( + let _ = write!( + output, concat!( " store.register_removed(\n", " \"clippy::{}\",\n", @@ -243,7 +476,7 @@ fn gen_deprecated(lints: &[DeprecatedLint]) -> String { " );\n" ), lint.name, lint.reason, - )); + ); } output.push_str("}\n"); @@ -269,25 +502,62 @@ fn gen_register_lint_list<'a>( if !is_public { output.push_str(" #[cfg(feature = \"internal\")]\n"); } - output.push_str(&format!(" {}::{},\n", module_name, lint_name)); + let _ = writeln!(output, " {}::{},", module_name, lint_name); } output.push_str("])\n"); output } +fn gen_deprecated_lints_test(lints: &[DeprecatedLint]) -> String { + let mut res: String = GENERATED_FILE_COMMENT.into(); + for lint in lints { + writeln!(res, "#![warn(clippy::{})]", lint.name).unwrap(); + } + res.push_str("\nfn main() {}\n"); + res +} + +fn gen_renamed_lints_test(lints: &[RenamedLint]) -> String { + let mut seen_lints = HashSet::new(); + let mut res: String = GENERATED_FILE_COMMENT.into(); + res.push_str("// run-rustfix\n\n"); + for lint in lints { + if seen_lints.insert(&lint.new_name) { + writeln!(res, "#![allow({})]", lint.new_name).unwrap(); + } + } + seen_lints.clear(); + for lint in lints { + if seen_lints.insert(&lint.old_name) { + writeln!(res, "#![warn({})]", lint.old_name).unwrap(); + } + } + res.push_str("\nfn main() {}\n"); + res +} + +fn gen_renamed_lints_list(lints: &[RenamedLint]) -> String { + const HEADER: &str = "\ + // This file is managed by `cargo dev rename_lint`. Prefer using that when possible.\n\n\ + #[rustfmt::skip]\n\ + pub static RENAMED_LINTS: &[(&str, &str)] = &[\n"; + + let mut res = String::from(HEADER); + for lint in lints { + writeln!(res, " (\"{}\", \"{}\"),", lint.old_name, lint.new_name).unwrap(); + } + res.push_str("];\n"); + res +} + /// Gathers all lints defined in `clippy_lints/src` -fn gather_all() -> (Vec, Vec) { +fn gather_all() -> (Vec, Vec, Vec) { let mut lints = Vec::with_capacity(1000); let mut deprecated_lints = Vec::with_capacity(50); - let root_path = clippy_project_root().join("clippy_lints/src"); + let mut renamed_lints = Vec::with_capacity(50); - for (rel_path, file) in WalkDir::new(&root_path) - .into_iter() - .map(Result::unwrap) - .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) - .map(|f| (f.path().strip_prefix(&root_path).unwrap().to_path_buf(), f)) - { + for (rel_path, file) in clippy_lints_src_files() { let path = file.path(); let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e)); @@ -305,13 +575,21 @@ fn gather_all() -> (Vec, Vec) { module.strip_suffix(".rs").unwrap_or(&module) }; - if module == "deprecated_lints" { - parse_deprecated_contents(&contents, &mut deprecated_lints); - } else { - parse_contents(&contents, module, &mut lints); + match module { + "deprecated_lints" => parse_deprecated_contents(&contents, &mut deprecated_lints), + "renamed_lints" => parse_renamed_contents(&contents, &mut renamed_lints), + _ => parse_contents(&contents, module, &mut lints), } } - (lints, deprecated_lints) + (lints, deprecated_lints, renamed_lints) +} + +fn clippy_lints_src_files() -> impl Iterator { + let root_path = clippy_project_root().join("clippy_lints/src"); + let iter = WalkDir::new(&root_path).into_iter(); + iter.map(Result::unwrap) + .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) + .map(move |f| (f.path().strip_prefix(&root_path).unwrap().to_path_buf(), f)) } macro_rules! match_tokens { @@ -394,6 +672,25 @@ fn parse_deprecated_contents(contents: &str, lints: &mut Vec) { } } +fn parse_renamed_contents(contents: &str, lints: &mut Vec) { + for line in contents.lines() { + let mut offset = 0usize; + let mut iter = tokenize(line).map(|t| { + let range = offset..offset + t.len; + offset = range.end; + (t.kind, &line[range]) + }); + let (old_name, new_name) = match_tokens!( + iter, + // ("old_name", + Whitespace OpenParen Literal{kind: LiteralKind::Str{..},..}(old_name) Comma + // "new_name"), + Whitespace Literal{kind: LiteralKind::Str{..},..}(new_name) CloseParen Comma + ); + lints.push(RenamedLint::new(old_name, new_name)); + } +} + /// Removes the line splices and surrounding quotes from a string literal fn remove_line_splices(s: &str) -> String { let s = s @@ -462,6 +759,52 @@ fn replace_region_in_text<'a>( Ok(res) } +fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { + match fs::OpenOptions::new().create_new(true).write(true).open(new_name) { + Ok(file) => drop(file), + Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false, + Err(e) => panic_file(e, new_name, "create"), + }; + match fs::rename(old_name, new_name) { + Ok(()) => true, + Err(e) => { + drop(fs::remove_file(new_name)); + if e.kind() == io::ErrorKind::NotFound { + false + } else { + panic_file(e, old_name, "rename"); + } + }, + } +} + +#[allow(clippy::needless_pass_by_value)] +fn panic_file(error: io::Error, name: &Path, action: &str) -> ! { + panic!("failed to {} file `{}`: {}", action, name.display(), error) +} + +fn rewrite_file(path: &Path, f: impl FnOnce(&str) -> Option) { + let mut file = fs::OpenOptions::new() + .write(true) + .read(true) + .open(path) + .unwrap_or_else(|e| panic_file(e, path, "open")); + let mut buf = String::new(); + file.read_to_string(&mut buf) + .unwrap_or_else(|e| panic_file(e, path, "read")); + if let Some(new_contents) = f(&buf) { + file.rewind().unwrap_or_else(|e| panic_file(e, path, "write")); + file.write_all(new_contents.as_bytes()) + .unwrap_or_else(|e| panic_file(e, path, "write")); + file.set_len(new_contents.len() as u64) + .unwrap_or_else(|e| panic_file(e, path, "write")); + } +} + +fn write_file(path: &Path, contents: &str) { + fs::write(path, contents).unwrap_or_else(|e| panic_file(e, path, "write")); +} + #[cfg(test)] mod tests { use super::*; diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index d0b03c0a9c2..8b0e11cb802 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -6,7 +6,7 @@ use clippy_utils::msrvs; use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments}; use clippy_utils::{extract_msrv_attr, meets_msrv}; use if_chain::if_chain; -use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; +use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MacArgs, MacArgsEq, MetaItemKind, NestedMetaItem}; use rustc_errors::Applicability; use rustc_hir::{ Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind, @@ -335,9 +335,6 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { } if let Some(lint_list) = &attr.meta_item_list() { if attr.ident().map_or(false, |ident| is_lint_level(ident.name)) { - // permit `unused_imports`, `deprecated`, `unreachable_pub`, - // `clippy::wildcard_imports`, and `clippy::enum_glob_use` for `use` items - // and `unused_imports` for `extern crate` items with `macro_use` for lint in lint_list { match item.kind { ItemKind::Use(..) => { @@ -345,10 +342,12 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { || is_word(lint, sym::deprecated) || is_word(lint, sym!(unreachable_pub)) || is_word(lint, sym!(unused)) - || extract_clippy_lint(lint) - .map_or(false, |s| s.as_str() == "wildcard_imports") - || extract_clippy_lint(lint) - .map_or(false, |s| s.as_str() == "enum_glob_use") + || extract_clippy_lint(lint).map_or(false, |s| { + matches!( + s.as_str(), + "wildcard_imports" | "enum_glob_use" | "redundant_pub_crate", + ) + }) { return; } @@ -594,6 +593,10 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::It }; if attr.style == AttrStyle::Outer { + if let MacArgs::Eq(_, MacArgsEq::Ast(expr)) = &attr_item.args + && !matches!(expr.kind, rustc_ast::ExprKind::Lit(..)) { + return; + } if attr_item.args.inner_tokens().is_empty() || !is_present_in_source(cx, attr.span) { return; } diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 4592ca72748..5b7c4591504 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -1,12 +1,15 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{match_def_path, paths}; +use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; -use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind}; +use rustc_hir::{def::Res, AsyncGeneratorKind, Body, BodyId, GeneratorKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::GeneratorInteriorTypeCause; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; +use crate::utils::conf::DisallowedType; + declare_clippy_lint! { /// ### What it does /// Checks for calls to await while holding a non-async-aware MutexGuard. @@ -127,9 +130,75 @@ declare_clippy_lint! { "inside an async function, holding a `RefCell` ref while calling `await`" } -declare_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF]); +declare_clippy_lint! { + /// ### What it does + /// Allows users to configure types which should not be held across `await` + /// suspension points. + /// + /// ### Why is this bad? + /// There are some types which are perfectly "safe" to be used concurrently + /// from a memory access perspective but will cause bugs at runtime if they + /// are held in such a way. + /// + /// ### Known problems + /// + /// ### Example + /// + /// ```toml + /// await-holding-invalid-types = [ + /// # You can specify a type name + /// "CustomLockType", + /// # You can (optionally) specify a reason + /// { path = "OtherCustomLockType", reason = "Relies on a thread local" } + /// ] + /// ``` + /// + /// ```rust + /// # async fn baz() {} + /// struct CustomLockType; + /// struct OtherCustomLockType; + /// async fn foo() { + /// let _x = CustomLockType; + /// let _y = OtherCustomLockType; + /// baz().await; // Lint violation + /// } + /// ``` + #[clippy::version = "1.49.0"] + pub AWAIT_HOLDING_INVALID_TYPE, + suspicious, + "holding a type across an await point which is not allowed to be held as per the configuration" +} + +impl_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF, AWAIT_HOLDING_INVALID_TYPE]); + +#[derive(Debug)] +pub struct AwaitHolding { + conf_invalid_types: Vec, + def_ids: FxHashMap, +} + +impl AwaitHolding { + pub(crate) fn new(conf_invalid_types: Vec) -> Self { + Self { + conf_invalid_types, + def_ids: FxHashMap::default(), + } + } +} impl LateLintPass<'_> for AwaitHolding { + fn check_crate(&mut self, cx: &LateContext<'_>) { + for conf in &self.conf_invalid_types { + let path = match conf { + DisallowedType::Simple(path) | DisallowedType::WithReason { path, .. } => path, + }; + let segs: Vec<_> = path.split("::").collect(); + if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs) { + self.def_ids.insert(id, conf.clone()); + } + } + } + fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { use AsyncGeneratorKind::{Block, Closure, Fn}; if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind { @@ -137,7 +206,7 @@ impl LateLintPass<'_> for AwaitHolding { hir_id: body.value.hir_id, }; let typeck_results = cx.tcx.typeck_body(body_id); - check_interior_types( + self.check_interior_types( cx, typeck_results.generator_interior_types.as_ref().skip_binder(), body.value.span, @@ -146,46 +215,68 @@ impl LateLintPass<'_> for AwaitHolding { } } -fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { - for ty_cause in ty_causes { - if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { - if is_mutex_guard(cx, adt.did()) { - span_lint_and_then( - cx, - AWAIT_HOLDING_LOCK, - ty_cause.span, - "this `MutexGuard` is held across an `await` point", - |diag| { - diag.help( - "consider using an async-aware `Mutex` type or ensuring the \ +impl AwaitHolding { + fn check_interior_types(&self, cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { + for ty_cause in ty_causes { + if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { + if is_mutex_guard(cx, adt.did()) { + span_lint_and_then( + cx, + AWAIT_HOLDING_LOCK, + ty_cause.span, + "this `MutexGuard` is held across an `await` point", + |diag| { + diag.help( + "consider using an async-aware `Mutex` type or ensuring the \ `MutexGuard` is dropped before calling await", - ); - diag.span_note( - ty_cause.scope_span.unwrap_or(span), - "these are all the `await` points this lock is held through", - ); - }, - ); - } - if is_refcell_ref(cx, adt.did()) { - span_lint_and_then( - cx, - AWAIT_HOLDING_REFCELL_REF, - ty_cause.span, - "this `RefCell` reference is held across an `await` point", - |diag| { - diag.help("ensure the reference is dropped before calling `await`"); - diag.span_note( - ty_cause.scope_span.unwrap_or(span), - "these are all the `await` points this reference is held through", - ); - }, - ); + ); + diag.span_note( + ty_cause.scope_span.unwrap_or(span), + "these are all the `await` points this lock is held through", + ); + }, + ); + } else if is_refcell_ref(cx, adt.did()) { + span_lint_and_then( + cx, + AWAIT_HOLDING_REFCELL_REF, + ty_cause.span, + "this `RefCell` reference is held across an `await` point", + |diag| { + diag.help("ensure the reference is dropped before calling `await`"); + diag.span_note( + ty_cause.scope_span.unwrap_or(span), + "these are all the `await` points this reference is held through", + ); + }, + ); + } else if let Some(disallowed) = self.def_ids.get(&adt.did()) { + emit_invalid_type(cx, ty_cause.span, disallowed); + } } } } } +fn emit_invalid_type(cx: &LateContext<'_>, span: Span, disallowed: &DisallowedType) { + let (type_name, reason) = match disallowed { + DisallowedType::Simple(path) => (path, &None), + DisallowedType::WithReason { path, reason } => (path, reason), + }; + + span_lint_and_then( + cx, + AWAIT_HOLDING_INVALID_TYPE, + span, + &format!("`{type_name}` may not be held across an `await` point per `clippy.toml`",), + |diag| { + if let Some(reason) = reason { + diag.note(reason.clone()); + } + }, + ); +} + fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool { match_def_path(cx, def_id, &paths::MUTEX_GUARD) || match_def_path(cx, def_id, &paths::RWLOCK_READ_GUARD) diff --git a/clippy_lints/src/bytes_count_to_len.rs b/clippy_lints/src/bytes_count_to_len.rs new file mode 100644 index 00000000000..d70dbf5b239 --- /dev/null +++ b/clippy_lints/src/bytes_count_to_len.rs @@ -0,0 +1,70 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{match_def_path, paths}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// It checks for `str::bytes().count()` and suggests replacing it with + /// `str::len()`. + /// + /// ### Why is this bad? + /// `str::bytes().count()` is longer and may not be as performant as using + /// `str::len()`. + /// + /// ### Example + /// ```rust + /// "hello".bytes().count(); + /// String::from("hello").bytes().count(); + /// ``` + /// Use instead: + /// ```rust + /// "hello".len(); + /// String::from("hello").len(); + /// ``` + #[clippy::version = "1.62.0"] + pub BYTES_COUNT_TO_LEN, + complexity, + "Using `bytes().count()` when `len()` performs the same functionality" +} + +declare_lint_pass!(BytesCountToLen => [BYTES_COUNT_TO_LEN]); + +impl<'tcx> LateLintPass<'tcx> for BytesCountToLen { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if_chain! { + if let hir::ExprKind::MethodCall(_, expr_args, _) = &expr.kind; + if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if match_def_path(cx, expr_def_id, &paths::ITER_COUNT); + + if let [bytes_expr] = &**expr_args; + if let hir::ExprKind::MethodCall(_, bytes_args, _) = &bytes_expr.kind; + if let Some(bytes_def_id) = cx.typeck_results().type_dependent_def_id(bytes_expr.hir_id); + if match_def_path(cx, bytes_def_id, &paths::STR_BYTES); + + if let [str_expr] = &**bytes_args; + let ty = cx.typeck_results().expr_ty(str_expr).peel_refs(); + + if is_type_diagnostic_item(cx, ty, sym::String) || ty.kind() == &ty::Str; + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + BYTES_COUNT_TO_LEN, + expr.span, + "using long and hard to read `.bytes().count()`", + "consider calling `.len()` instead", + format!("{}.len()", snippet_with_applicability(cx, str_expr.span, "..", &mut applicability)), + applicability + ); + } + }; + } +} diff --git a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs index a8f9c189ade..7af200708ff 100644 --- a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs @@ -42,8 +42,8 @@ fn check_case_sensitive_file_extension_comparison(ctx: &LateContext<'_>, expr: & if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = extension.kind; if (2..=6).contains(&ext_literal.as_str().len()); if ext_literal.as_str().starts_with('.'); - if ext_literal.as_str().chars().skip(1).all(|c| c.is_uppercase() || c.is_digit(10)) - || ext_literal.as_str().chars().skip(1).all(|c| c.is_lowercase() || c.is_digit(10)); + if ext_literal.as_str().chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit()) + || ext_literal.as_str().chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit()); then { let mut ty = ctx.typeck_results().expr_ty(obj); ty = match ty.kind() { diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index 421bd6f53f7..64f87c80f8d 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -29,21 +29,19 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b ExprKind::Block(block, _) => block.expr.map_or(nbits, |e| apply_reductions(cx, nbits, e, signed)), ExprKind::Binary(op, left, right) => match op.node { BinOpKind::Div => { - apply_reductions(cx, nbits, left, signed) - - (if signed { - 0 // let's be conservative here - } else { - // by dividing by 1, we remove 0 bits, etc. - get_constant_bits(cx, right).map_or(0, |b| b.saturating_sub(1)) - }) + apply_reductions(cx, nbits, left, signed).saturating_sub(if signed { + // let's be conservative here + 0 + } else { + // by dividing by 1, we remove 0 bits, etc. + get_constant_bits(cx, right).map_or(0, |b| b.saturating_sub(1)) + }) }, BinOpKind::Rem | BinOpKind::BitAnd => get_constant_bits(cx, right) .unwrap_or(u64::max_value()) .min(apply_reductions(cx, nbits, left, signed)), - BinOpKind::Shr => { - apply_reductions(cx, nbits, left, signed) - - constant_int(cx, right).map_or(0, |s| u64::try_from(s).expect("shift too high")) - }, + BinOpKind::Shr => apply_reductions(cx, nbits, left, signed) + .saturating_sub(constant_int(cx, right).map_or(0, |s| u64::try_from(s).expect("shift too high"))), _ => nbits, }, ExprKind::MethodCall(method, [left, right], _) => { diff --git a/clippy_lints/src/casts/cast_slice_different_sizes.rs b/clippy_lints/src/casts/cast_slice_different_sizes.rs index 3608c1654d5..2238668abca 100644 --- a/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -1,4 +1,4 @@ -use clippy_utils::{diagnostics::span_lint_and_then, meets_msrv, msrvs, source::snippet_opt}; +use clippy_utils::{diagnostics::span_lint_and_then, meets_msrv, msrvs, source}; use if_chain::if_chain; use rustc_ast::Mutability; use rustc_hir::{Expr, ExprKind, Node}; @@ -8,6 +8,63 @@ use rustc_semver::RustcVersion; use super::CAST_SLICE_DIFFERENT_SIZES; +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Option) { + // suggestion is invalid if `ptr::slice_from_raw_parts` does not exist + if !meets_msrv(msrv.as_ref(), &msrvs::PTR_SLICE_RAW_PARTS) { + return; + } + + // if this cast is the child of another cast expression then don't emit something for it, the full + // chain will be analyzed + if is_child_of_cast(cx, expr) { + return; + } + + if let Some(CastChainInfo { + left_cast, + start_ty, + end_ty, + }) = expr_cast_chain_tys(cx, expr) + { + if let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(start_ty.ty), cx.layout_of(end_ty.ty)) { + let from_size = from_layout.size.bytes(); + let to_size = to_layout.size.bytes(); + if from_size != to_size && from_size != 0 && to_size != 0 { + span_lint_and_then( + cx, + CAST_SLICE_DIFFERENT_SIZES, + expr.span, + &format!( + "casting between raw pointers to `[{}]` (element size {}) and `[{}]` (element size {}) does not adjust the count", + start_ty.ty, from_size, end_ty.ty, to_size, + ), + |diag| { + let ptr_snippet = source::snippet(cx, left_cast.span, ".."); + + let (mutbl_fn_str, mutbl_ptr_str) = match end_ty.mutbl { + Mutability::Mut => ("_mut", "mut"), + Mutability::Not => ("", "const"), + }; + let sugg = format!( + "core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {}, ..)", + // get just the ty from the TypeAndMut so that the printed type isn't something like `mut + // T`, extract just the `T` + end_ty.ty + ); + + diag.span_suggestion( + expr.span, + &format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"), + sugg, + rustc_errors::Applicability::HasPlaceholders, + ); + }, + ); + } + } + } +} + fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let map = cx.tcx.hir(); if_chain! { @@ -33,59 +90,6 @@ fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } } -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option) { - // suggestion is invalid if `ptr::slice_from_raw_parts` does not exist - if !meets_msrv(msrv.as_ref(), &msrvs::PTR_SLICE_RAW_PARTS) { - return; - } - - // if this cast is the child of another cast expression then don't emit something for it, the full - // chain will be analyzed - if is_child_of_cast(cx, expr) { - return; - } - - if let Some((from_slice_ty, to_slice_ty)) = expr_cast_chain_tys(cx, expr) { - if let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(from_slice_ty.ty), cx.layout_of(to_slice_ty.ty)) { - let from_size = from_layout.size.bytes(); - let to_size = to_layout.size.bytes(); - if from_size != to_size && from_size != 0 && to_size != 0 { - span_lint_and_then( - cx, - CAST_SLICE_DIFFERENT_SIZES, - expr.span, - &format!( - "casting between raw pointers to `[{}]` (element size {}) and `[{}]` (element size {}) does not adjust the count", - from_slice_ty, from_size, to_slice_ty, to_size, - ), - |diag| { - let cast_expr = match expr.kind { - ExprKind::Cast(cast_expr, ..) => cast_expr, - _ => unreachable!("expr should be a cast as checked by expr_cast_chain_tys"), - }; - let ptr_snippet = snippet_opt(cx, cast_expr.span).unwrap(); - - let (mutbl_fn_str, mutbl_ptr_str) = match to_slice_ty.mutbl { - Mutability::Mut => ("_mut", "mut"), - Mutability::Not => ("", "const"), - }; - let sugg = format!( - "core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {to_slice_ty}, ..)" - ); - - diag.span_suggestion( - expr.span, - &format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"), - sugg, - rustc_errors::Applicability::HasPlaceholders, - ); - }, - ); - } - } - } -} - /// Returns the type T of the pointed to *const [T] or *mut [T] and the mutability of the slice if /// the type is one of those slices fn get_raw_slice_ty_mut(ty: Ty<'_>) -> Option> { @@ -98,18 +102,40 @@ fn get_raw_slice_ty_mut(ty: Ty<'_>) -> Option> { } } -/// Returns the pair (original ptr T, final ptr U) if the expression is composed of casts +struct CastChainInfo<'tcx> { + /// The left most part of the cast chain, or in other words, the first cast in the chain + /// Used for diagnostics + left_cast: &'tcx Expr<'tcx>, + /// The starting type of the cast chain + start_ty: TypeAndMut<'tcx>, + /// The final type of the cast chain + end_ty: TypeAndMut<'tcx>, +} + +/// Returns a `CastChainInfo` with the left-most cast in the chain and the original ptr T and final +/// ptr U if the expression is composed of casts. /// Returns None if the expr is not a Cast -fn expr_cast_chain_tys<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<(TypeAndMut<'tcx>, TypeAndMut<'tcx>)> { +fn expr_cast_chain_tys<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option> { if let ExprKind::Cast(cast_expr, _cast_to_hir_ty) = expr.peel_blocks().kind { let cast_to = cx.typeck_results().expr_ty(expr); let to_slice_ty = get_raw_slice_ty_mut(cast_to)?; - if let Some((inner_from_ty, _inner_to_ty)) = expr_cast_chain_tys(cx, cast_expr) { - Some((inner_from_ty, to_slice_ty)) + + // If the expression that makes up the source of this cast is itself a cast, recursively + // call `expr_cast_chain_tys` and update the end type with the final tartet type. + // Otherwise, this cast is not immediately nested, just construct the info for this cast + if let Some(prev_info) = expr_cast_chain_tys(cx, cast_expr) { + Some(CastChainInfo { + end_ty: to_slice_ty, + ..prev_info + }) } else { let cast_from = cx.typeck_results().expr_ty(cast_expr); let from_slice_ty = get_raw_slice_ty_mut(cast_from)?; - Some((from_slice_ty, to_slice_ty)) + Some(CastChainInfo { + left_cast: cast_expr, + start_ty: from_slice_ty, + end_ty: to_slice_ty, + }) } } else { None diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 55c1f085657..b58252dcb94 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -270,7 +270,7 @@ declare_clippy_lint! { /// /// ### Why is this bad? /// Casting a function pointer to an integer can have surprising results and can occur - /// accidentally if parantheses are omitted from a function call. If you aren't doing anything + /// accidentally if parentheses are omitted from a function call. If you aren't doing anything /// low-level with function pointers then you can opt-out of casting functions to integers in /// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function /// pointer casts in your code. @@ -487,6 +487,7 @@ declare_clippy_lint! { /// let y: u32 = x.abs() as u32; /// ``` /// Use instead: + /// ```rust /// let x: i32 = -42; /// let y: u32 = x.unsigned_abs(); /// ``` diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index eae2723a7da..3227e6e86af 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -13,13 +13,14 @@ //! This lint is **warn** by default use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::{snippet_block, snippet_block_with_applicability}; +use clippy_utils::source::{snippet, snippet_block, snippet_block_with_applicability}; use clippy_utils::sugg::Sugg; use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -102,7 +103,7 @@ impl EarlyLintPass for CollapsibleIf { fn check_if(cx: &EarlyContext<'_>, expr: &ast::Expr) { if let ast::ExprKind::If(check, then, else_) = &expr.kind { if let Some(else_) = else_ { - check_collapsible_maybe_if_let(cx, else_); + check_collapsible_maybe_if_let(cx, then.span, else_); } else if let ast::ExprKind::Let(..) = check.kind { // Prevent triggering on `if let a = b { if c { .. } }`. } else { @@ -119,7 +120,7 @@ fn block_starts_with_comment(cx: &EarlyContext<'_>, expr: &ast::Block) -> bool { trimmed_block_text.starts_with("//") || trimmed_block_text.starts_with("/*") } -fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) { +fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, then_span: Span, else_: &ast::Expr) { if_chain! { if let ast::ExprKind::Block(ref block, _) = else_.kind; if !block_starts_with_comment(cx, block); @@ -128,6 +129,11 @@ fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) { if !else_.span.from_expansion(); if let ast::ExprKind::If(..) = else_.kind; then { + // Prevent "elseif" + // Check that the "else" is followed by whitespace + let up_to_else = then_span.between(block.span); + let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() { !c.is_whitespace() } else { false }; + let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, @@ -135,7 +141,11 @@ fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) { block.span, "this `else { if .. }` block can be collapsed", "collapse nested if block", - snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability).into_owned(), + format!( + "{}{}", + if requires_space { " " } else { "" }, + snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability) + ), applicability, ); } diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 503cef76775..b3fd8af4730 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -240,7 +240,7 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { lint_for_missing_headers(cx, item.def_id, item.span, sig, headers, Some(body_id), fpu.panic_span); } }, - hir::ItemKind::Impl(ref impl_) => { + hir::ItemKind::Impl(impl_) => { self.in_trait_impl = impl_.of_trait.is_some(); }, hir::ItemKind::Trait(_, unsafety, ..) => { @@ -621,10 +621,8 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { let filename = FileName::anon_source_code(&code); let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let fallback_bundle = rustc_errors::fallback_fluent_bundle( - rustc_errors::DEFAULT_LOCALE_RESOURCES, - false - ); + let fallback_bundle = + rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false); let emitter = EmitterWriter::new( Box::new(io::sink()), None, @@ -741,7 +739,7 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span) { /// letters (`Clippy` is ok) and one lower-case letter (`NASA` is ok). /// Plurals are also excluded (`IDs` is ok). fn is_camel_case(s: &str) -> bool { - if s.starts_with(|c: char| c.is_digit(10)) { + if s.starts_with(|c: char| c.is_ascii_digit()) { return false; } diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index 88c54828da8..25014bfa1a5 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -180,7 +180,7 @@ const DROP_COPY_SUMMARY: &str = "calls to `std::mem::drop` with a value that imp const FORGET_COPY_SUMMARY: &str = "calls to `std::mem::forget` with a value that implements `Copy`. \ Forgetting a copy leaves the original intact"; const DROP_NON_DROP_SUMMARY: &str = "call to `std::mem::drop` with a value that does not implement `Drop`. \ - Dropping such a type only extends it's contained lifetimes"; + Dropping such a type only extends its contained lifetimes"; const FORGET_NON_DROP_SUMMARY: &str = "call to `std::mem::forget` with a value that does not implement `Drop`. \ Forgetting such a type is the same as dropping it"; diff --git a/clippy_lints/src/empty_drop.rs b/clippy_lints/src/empty_drop.rs new file mode 100644 index 00000000000..325ae2356c1 --- /dev/null +++ b/clippy_lints/src/empty_drop.rs @@ -0,0 +1,65 @@ +use clippy_utils::{diagnostics::span_lint_and_sugg, peel_blocks}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Body, ExprKind, Impl, ImplItemKind, Item, ItemKind, Node}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for empty `Drop` implementations. + /// + /// ### Why is this bad? + /// Empty `Drop` implementations have no effect when dropping an instance of the type. They are + /// most likely useless. However, an empty `Drop` implementation prevents a type from being + /// destructured, which might be the intention behind adding the implementation as a marker. + /// + /// ### Example + /// ```rust + /// struct S; + /// + /// impl Drop for S { + /// fn drop(&mut self) {} + /// } + /// ``` + /// Use instead: + /// ```rust + /// struct S; + /// ``` + #[clippy::version = "1.61.0"] + pub EMPTY_DROP, + restriction, + "empty `Drop` implementations" +} +declare_lint_pass!(EmptyDrop => [EMPTY_DROP]); + +impl LateLintPass<'_> for EmptyDrop { + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if_chain! { + if let ItemKind::Impl(Impl { + of_trait: Some(ref trait_ref), + items: [child], + .. + }) = item.kind; + if trait_ref.trait_def_id() == cx.tcx.lang_items().drop_trait(); + if let impl_item_hir = child.id.hir_id(); + if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir); + if let ImplItemKind::Fn(_, b) = &impl_item.kind; + if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b); + let func_expr = peel_blocks(func_expr); + if let ExprKind::Block(block, _) = func_expr.kind; + if block.stmts.is_empty() && block.expr.is_none(); + then { + span_lint_and_sugg( + cx, + EMPTY_DROP, + item.span, + "empty drop implementation", + "try removing this impl", + String::new(), + Applicability::MaybeIncorrect + ); + } + } + } +} diff --git a/clippy_lints/src/empty_structs_with_brackets.rs b/clippy_lints/src/empty_structs_with_brackets.rs index fdeac8d8255..8430e7b4c82 100644 --- a/clippy_lints/src/empty_structs_with_brackets.rs +++ b/clippy_lints/src/empty_structs_with_brackets.rs @@ -66,7 +66,7 @@ fn has_no_fields(cx: &EarlyContext<'_>, var_data: &VariantData, braces_span: Spa } // there might still be field declarations hidden from the AST - // (conditionaly compiled code using #[cfg(..)]) + // (conditionally compiled code using #[cfg(..)]) let Some(braces_span_str) = snippet_opt(cx, braces_span) else { return false; diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 845863bd209..1b19868e4c7 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -3,7 +3,7 @@ use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::local_used_after_expr; -use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local, path_to_local_id}; +use clippy_utils::{higher, is_adjusted, path_to_local, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; @@ -103,6 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { let closure_ty = cx.typeck_results().expr_ty(expr); if_chain!( + if !is_adjusted(cx, &body.value); if let ExprKind::Call(callee, args) = body.value.kind; if let ExprKind::Path(_) = callee.kind; if check_inputs(cx, body.params, args); @@ -125,8 +126,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { if_chain! { if let ty::Closure(_, substs) = callee_ty.peel_refs().kind(); if substs.as_closure().kind() == ClosureKind::FnMut; - if get_enclosing_loop_or_closure(cx.tcx, expr).is_some() - || path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, callee)); + if path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr)); then { // Mutable closure is used after current expr; we cannot consume it. @@ -145,6 +145,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { ); if_chain!( + if !is_adjusted(cx, &body.value); if let ExprKind::MethodCall(path, args, _) = body.value.kind; if check_inputs(cx, body.params, args); let method_def_id = cx.typeck_results().type_dependent_def_id(body.value.hir_id).unwrap(); diff --git a/clippy_lints/src/format_push_string.rs b/clippy_lints/src/format_push_string.rs new file mode 100644 index 00000000000..ee15ae9f59a --- /dev/null +++ b/clippy_lints/src/format_push_string.rs @@ -0,0 +1,77 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{match_def_path, paths, peel_hir_expr_refs}; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Detects cases where the result of a `format!` call is + /// appended to an existing `String`. + /// + /// ### Why is this bad? + /// Introduces an extra, avoidable heap allocation. + /// + /// ### Example + /// ```rust + /// let mut s = String::new(); + /// s += &format!("0x{:X}", 1024); + /// s.push_str(&format!("0x{:X}", 1024)); + /// ``` + /// Use instead: + /// ```rust + /// use std::fmt::Write as _; // import without risk of name clashing + /// + /// let mut s = String::new(); + /// let _ = write!(s, "0x{:X}", 1024); + /// ``` + #[clippy::version = "1.61.0"] + pub FORMAT_PUSH_STRING, + perf, + "`format!(..)` appended to existing `String`" +} +declare_lint_pass!(FormatPushString => [FORMAT_PUSH_STRING]); + +fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { + is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), sym::String) +} +fn is_format(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { + if let Some(macro_def_id) = e.span.ctxt().outer_expn_data().macro_def_id { + cx.tcx.get_diagnostic_name(macro_def_id) == Some(sym::format_macro) + } else { + false + } +} + +impl<'tcx> LateLintPass<'tcx> for FormatPushString { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + let arg = match expr.kind { + ExprKind::MethodCall(_, [_, arg], _) => { + if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && + match_def_path(cx, fn_def_id, &paths::PUSH_STR) { + arg + } else { + return; + } + } + ExprKind::AssignOp(op, left, arg) + if op.node == BinOpKind::Add && is_string(cx, left) => { + arg + }, + _ => return, + }; + let (arg, _) = peel_hir_expr_refs(arg); + if is_format(cx, arg) { + span_lint_and_help( + cx, + FORMAT_PUSH_STRING, + expr.span, + "`format!(..)` appended to existing `String`", + None, + "consider using `write!` to avoid the extra allocation", + ); + } + } +} diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index 5462d913fb4..38e943d2eb8 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -20,7 +20,7 @@ use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT}; pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); let attr = must_use_attr(attrs); - if let hir::ItemKind::Fn(ref sig, ref _generics, ref body_id) = item.kind { + if let hir::ItemKind::Fn(ref sig, _generics, ref body_id) = item.kind { let is_public = cx.access_levels.is_exported(item.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); if let Some(attr) = attr { @@ -105,12 +105,7 @@ fn check_needless_must_use( fn_header_span, "this unit-returning function has a `#[must_use]` attribute", |diag| { - diag.span_suggestion( - attr.span, - "remove the attribute", - "", - Applicability::MachineApplicable, - ); + diag.span_suggestion(attr.span, "remove the attribute", "", Applicability::MachineApplicable); }, ); } else if attr.value_str().is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) { diff --git a/clippy_lints/src/functions/result_unit_err.rs b/clippy_lints/src/functions/result_unit_err.rs index 120fcb2619c..2e63a1f920d 100644 --- a/clippy_lints/src/functions/result_unit_err.rs +++ b/clippy_lints/src/functions/result_unit_err.rs @@ -14,7 +14,7 @@ use clippy_utils::ty::is_type_diagnostic_item; use super::RESULT_UNIT_ERR; pub(super) fn check_item(cx: &LateContext<'_>, item: &hir::Item<'_>) { - if let hir::ItemKind::Fn(ref sig, ref _generics, _) = item.kind { + if let hir::ItemKind::Fn(ref sig, _generics, _) = item.kind { let is_public = cx.access_levels.is_exported(item.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); if is_public { diff --git a/clippy_lints/src/identity_op.rs b/clippy_lints/src/identity_op.rs index 4d6bef89bea..40cc5cd4bcf 100644 --- a/clippy_lints/src/identity_op.rs +++ b/clippy_lints/src/identity_op.rs @@ -1,3 +1,4 @@ +use clippy_utils::get_parent_expr; use clippy_utils::source::snippet; use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -22,6 +23,11 @@ declare_clippy_lint! { /// # let x = 1; /// x / 1 + 0 * 1 - 0 | 0; /// ``` + /// + /// ### Known problems + /// False negatives: `f(0 + if b { 1 } else { 2 } + 3);` is reducible to + /// `f(if b { 1 } else { 2 } + 3);`. But the lint doesn't trigger for the code. + /// See [#8724](https://github.com/rust-lang/rust-clippy/issues/8724) #[clippy::version = "pre 1.29.0"] pub IDENTITY_OP, complexity, @@ -31,36 +37,66 @@ declare_clippy_lint! { declare_lint_pass!(IdentityOp => [IDENTITY_OP]); impl<'tcx> LateLintPass<'tcx> for IdentityOp { - fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if e.span.from_expansion() { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if expr.span.from_expansion() { return; } - if let ExprKind::Binary(cmp, left, right) = e.kind { - if is_allowed(cx, cmp, left, right) { - return; - } - match cmp.node { - BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => { - check(cx, left, 0, e.span, right.span); - check(cx, right, 0, e.span, left.span); - }, - BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => check(cx, right, 0, e.span, left.span), - BinOpKind::Mul => { - check(cx, left, 1, e.span, right.span); - check(cx, right, 1, e.span, left.span); - }, - BinOpKind::Div => check(cx, right, 1, e.span, left.span), - BinOpKind::BitAnd => { - check(cx, left, -1, e.span, right.span); - check(cx, right, -1, e.span, left.span); - }, - BinOpKind::Rem => check_remainder(cx, left, right, e.span, left.span), - _ => (), + if let ExprKind::Binary(cmp, left, right) = &expr.kind { + if !is_allowed(cx, *cmp, left, right) { + match cmp.node { + BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => { + if reducible_to_right(cx, expr, right) { + check(cx, left, 0, expr.span, right.span); + } + check(cx, right, 0, expr.span, left.span); + }, + BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => { + check(cx, right, 0, expr.span, left.span); + }, + BinOpKind::Mul => { + if reducible_to_right(cx, expr, right) { + check(cx, left, 1, expr.span, right.span); + } + check(cx, right, 1, expr.span, left.span); + }, + BinOpKind::Div => check(cx, right, 1, expr.span, left.span), + BinOpKind::BitAnd => { + if reducible_to_right(cx, expr, right) { + check(cx, left, -1, expr.span, right.span); + } + check(cx, right, -1, expr.span, left.span); + }, + BinOpKind::Rem => { + // Don't call reducible_to_right because N % N is always reducible to 1 + check_remainder(cx, left, right, expr.span, left.span); + }, + _ => (), + } } } } } +/// Checks if `left op ..right` can be actually reduced to `right` +/// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` +/// cannot be reduced to `if b { 1 } else { 2 } + if b { 3 } else { 4 }` +/// See #8724 +fn reducible_to_right(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>) -> bool { + if let ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) | ExprKind::Loop(..) = right.kind { + is_toplevel_binary(cx, binary) + } else { + true + } +} + +fn is_toplevel_binary(cx: &LateContext<'_>, must_be_binary: &Expr<'_>) -> bool { + if let Some(parent) = get_parent_expr(cx, must_be_binary) && let ExprKind::Binary(..) = &parent.kind { + false + } else { + true + } +} + fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> bool { // This lint applies to integers !cx.typeck_results().expr_ty(left).peel_refs().is_integral() diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index d5430a8c917..feb1b1014b1 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -117,7 +117,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { } match item.kind { - ItemKind::Impl(ref impl_) => { + ItemKind::Impl(impl_) => { let mut vis = ImplicitHasherTypeVisitor::new(cx); vis.visit_ty(impl_.self_ty); @@ -155,7 +155,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { ); } }, - ItemKind::Fn(ref sig, ref generics, body_id) => { + ItemKind::Fn(ref sig, generics, body_id) => { let body = cx.tcx.hir().body(body_id); for ty in sig.decl.inputs { diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs index c8ec2f45137..14b22d2b50d 100644 --- a/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/clippy_lints/src/inconsistent_struct_constructor.rs @@ -7,6 +7,7 @@ use rustc_hir::{self as hir, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Symbol; +use std::fmt::Write as _; declare_clippy_lint! { /// ### What it does @@ -89,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { let mut fields_snippet = String::new(); let (last_ident, idents) = ordered_fields.split_last().unwrap(); for ident in idents { - fields_snippet.push_str(&format!("{}, ", ident)); + let _ = write!(fields_snippet, "{}, ", ident); } fields_snippet.push_str(&last_ident.to_string()); diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 6b62748ffef..8a84513b779 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -116,7 +116,7 @@ fn find_slice_values(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> FxIndexMap LateLintPass<'tcx> for MultipleInherentImpl { fn get_impl_span(cx: &LateContext<'_>, id: LocalDefId) -> Option { let id = cx.tcx.hir().local_def_id_to_hir_id(id); if let Node::Item(&Item { - kind: ItemKind::Impl(ref impl_item), + kind: ItemKind::Impl(impl_item), span, .. }) = cx.tcx.hir().get(id) diff --git a/clippy_lints/src/init_numbered_fields.rs b/clippy_lints/src/init_numbered_fields.rs index 9284e002409..7e1548531f1 100644 --- a/clippy_lints/src/init_numbered_fields.rs +++ b/clippy_lints/src/init_numbered_fields.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -49,6 +50,7 @@ impl<'tcx> LateLintPass<'tcx> for NumberedFields { && fields .iter() .all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit)) + && !matches!(cx.qpath_res(path, e.hir_id), Res::Def(DefKind::TyAlias, ..)) { let expr_spans = fields .iter() diff --git a/clippy_lints/src/large_include_file.rs b/clippy_lints/src/large_include_file.rs new file mode 100644 index 00000000000..8bef13c682d --- /dev/null +++ b/clippy_lints/src/large_include_file.rs @@ -0,0 +1,86 @@ +use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::is_lint_allowed; +use clippy_utils::macros::root_macro_call_first_node; +use rustc_ast::LitKind; +use rustc_hir::Expr; +use rustc_hir::ExprKind; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Checks for the inclusion of large files via `include_bytes!()` + /// and `include_str!()` + /// + /// ### Why is this bad? + /// Including large files can increase the size of the binary + /// + /// ### Example + /// ```rust,ignore + /// let included_str = include_str!("very_large_file.txt"); + /// let included_bytes = include_bytes!("very_large_file.txt"); + /// ``` + /// + /// Instead, you can load the file at runtime: + /// ```rust,ignore + /// use std::fs; + /// + /// let string = fs::read_to_string("very_large_file.txt")?; + /// let bytes = fs::read("very_large_file.txt")?; + /// ``` + #[clippy::version = "1.62.0"] + pub LARGE_INCLUDE_FILE, + restriction, + "including a large file" +} + +pub struct LargeIncludeFile { + max_file_size: u64, +} + +impl LargeIncludeFile { + #[must_use] + pub fn new(max_file_size: u64) -> Self { + Self { max_file_size } + } +} + +impl_lint_pass!(LargeIncludeFile => [LARGE_INCLUDE_FILE]); + +impl LateLintPass<'_> for LargeIncludeFile { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { + if_chain! { + if let Some(macro_call) = root_macro_call_first_node(cx, expr); + if !is_lint_allowed(cx, LARGE_INCLUDE_FILE, expr.hir_id); + if cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id) + || cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id); + if let ExprKind::Lit(lit) = &expr.kind; + then { + let len = match &lit.node { + // include_bytes + LitKind::ByteStr(bstr) => bstr.len(), + // include_str + LitKind::Str(sym, _) => sym.as_str().len(), + _ => return, + }; + + if len as u64 <= self.max_file_size { + return; + } + + span_lint_and_note( + cx, + LARGE_INCLUDE_FILE, + expr.span, + "attempted to include a large file", + None, + &format!( + "the configuration allows a maximum size of {} bytes", + self.max_file_size + ), + ); + } + } + } +} diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 14ca93b5f3c..e68718f9fdf 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -14,6 +14,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(attrs::DEPRECATED_SEMVER), LintId::of(attrs::MISMATCHED_TARGET_OS), LintId::of(attrs::USELESS_ATTRIBUTE), + LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE), LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK), LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(bit_mask::BAD_BIT_MASK), @@ -23,6 +24,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON), LintId::of(booleans::LOGIC_BUG), LintId::of(booleans::NONMINIMAL_BOOL), + LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN), LintId::of(casts::CAST_ABS_TO_UNSIGNED), LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_TRUNCATION), @@ -77,6 +79,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS), LintId::of(format_impl::PRINT_IN_FORMAT_IMPL), LintId::of(format_impl::RECURSIVE_FORMAT_IMPL), + LintId::of(format_push_string::FORMAT_PUSH_STRING), LintId::of(formatting::POSSIBLE_MISSING_COMMA), LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING), @@ -165,6 +168,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(methods::FLAT_MAP_IDENTITY), LintId::of(methods::INSPECT_FOR_EACH), LintId::of(methods::INTO_ITER_ON_REF), + LintId::of(methods::IS_DIGIT_ASCII_RADIX), LintId::of(methods::ITERATOR_STEP_BY_ZERO), LintId::of(methods::ITER_CLONED_COLLECT), LintId::of(methods::ITER_COUNT), @@ -182,6 +186,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(methods::MAP_FLATTEN), LintId::of(methods::MAP_IDENTITY), LintId::of(methods::NEEDLESS_OPTION_AS_DEREF), + LintId::of(methods::NEEDLESS_OPTION_TAKE), LintId::of(methods::NEEDLESS_SPLITN), LintId::of(methods::NEW_RET_NO_SELF), LintId::of(methods::OK_EXPECT), @@ -243,7 +248,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), LintId::of(octal_escapes::OCTAL_ESCAPES), - LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION), LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP), LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), @@ -275,8 +279,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), - LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(strings::STRING_FROM_UTF8_AS_BYTES), + LintId::of(strings::TRIM_SPLIT_WHITESPACE), LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS), LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), @@ -306,10 +310,12 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(uninit_vec::UNINIT_VEC), LintId::of(unit_hash::UNIT_HASH), LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), + LintId::of(unit_types::LET_UNIT_VALUE), LintId::of(unit_types::UNIT_ARG), LintId::of(unit_types::UNIT_CMP), LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS), + LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS), LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(unused_io_amount::UNUSED_IO_AMOUNT), diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs index 10369a855ae..6f3c433af31 100644 --- a/clippy_lints/src/lib.register_complexity.rs +++ b/clippy_lints/src/lib.register_complexity.rs @@ -5,6 +5,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![ LintId::of(attrs::DEPRECATED_CFG_ATTR), LintId::of(booleans::NONMINIMAL_BOOL), + LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN), LintId::of(casts::CHAR_LIT_AS_U8), LintId::of(casts::UNNECESSARY_CAST), LintId::of(derivable_impls::DERIVABLE_IMPLS), @@ -45,6 +46,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(methods::MAP_FLATTEN), LintId::of(methods::MAP_IDENTITY), LintId::of(methods::NEEDLESS_OPTION_AS_DEREF), + LintId::of(methods::NEEDLESS_OPTION_TAKE), LintId::of(methods::NEEDLESS_SPLITN), LintId::of(methods::OPTION_AS_REF_DEREF), LintId::of(methods::OPTION_FILTER_MAP), @@ -66,7 +68,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), LintId::of(no_effect::NO_EFFECT), LintId::of(no_effect::UNNECESSARY_OPERATION), - LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION), LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL), LintId::of(precedence::PRECEDENCE), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 532590aaa5a..5768edc5018 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -52,6 +52,7 @@ store.register_lints(&[ attrs::INLINE_ALWAYS, attrs::MISMATCHED_TARGET_OS, attrs::USELESS_ATTRIBUTE, + await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE, await_holding_invalid::AWAIT_HOLDING_LOCK, await_holding_invalid::AWAIT_HOLDING_REFCELL_REF, bit_mask::BAD_BIT_MASK, @@ -64,6 +65,7 @@ store.register_lints(&[ booleans::NONMINIMAL_BOOL, borrow_as_ptr::BORROW_AS_PTR, bytecount::NAIVE_BYTECOUNT, + bytes_count_to_len::BYTES_COUNT_TO_LEN, cargo::CARGO_COMMON_METADATA, cargo::MULTIPLE_CRATE_VERSIONS, cargo::NEGATIVE_FEATURE_NAMES, @@ -132,6 +134,7 @@ store.register_lints(&[ drop_forget_ref::UNDROPPED_MANUALLY_DROPS, duration_subsec::DURATION_SUBSEC, else_if_without_else::ELSE_IF_WITHOUT_ELSE, + empty_drop::EMPTY_DROP, empty_enum::EMPTY_ENUM, empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS, entry::MAP_ENTRY, @@ -165,6 +168,7 @@ store.register_lints(&[ format_args::TO_STRING_IN_FORMAT_ARGS, format_impl::PRINT_IN_FORMAT_IMPL, format_impl::RECURSIVE_FORMAT_IMPL, + format_push_string::FORMAT_PUSH_STRING, formatting::POSSIBLE_MISSING_COMMA, formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING, formatting::SUSPICIOUS_ELSE_FORMATTING, @@ -205,6 +209,7 @@ store.register_lints(&[ iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR, large_const_arrays::LARGE_CONST_ARRAYS, large_enum_variant::LARGE_ENUM_VARIANT, + large_include_file::LARGE_INCLUDE_FILE, large_stack_arrays::LARGE_STACK_ARRAYS, len_zero::COMPARISON_TO_EMPTY, len_zero::LEN_WITHOUT_IS_EMPTY, @@ -302,6 +307,7 @@ store.register_lints(&[ methods::INEFFICIENT_TO_STRING, methods::INSPECT_FOR_EACH, methods::INTO_ITER_ON_REF, + methods::IS_DIGIT_ASCII_RADIX, methods::ITERATOR_STEP_BY_ZERO, methods::ITER_CLONED_COLLECT, methods::ITER_COUNT, @@ -321,6 +327,7 @@ store.register_lints(&[ methods::MAP_IDENTITY, methods::MAP_UNWRAP_OR, methods::NEEDLESS_OPTION_AS_DEREF, + methods::NEEDLESS_OPTION_TAKE, methods::NEEDLESS_SPLITN, methods::NEW_RET_NO_SELF, methods::OK_EXPECT, @@ -432,6 +439,7 @@ store.register_lints(&[ ptr::PTR_ARG, ptr_eq::PTR_EQ, ptr_offset_with_cast::PTR_OFFSET_WITH_CAST, + pub_use::PUB_USE, question_mark::QUESTION_MARK, ranges::MANUAL_RANGE_CONTAINS, ranges::RANGE_MINUS_ONE, @@ -474,6 +482,7 @@ store.register_lints(&[ strings::STRING_SLICE, strings::STRING_TO_STRING, strings::STR_TO_STRING, + strings::TRIM_SPLIT_WHITESPACE, strlen_on_c_strings::STRLEN_ON_C_STRINGS, suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS, suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL, @@ -523,6 +532,7 @@ store.register_lints(&[ unit_types::UNIT_CMP, unnamed_address::FN_ADDRESS_COMPARISONS, unnamed_address::VTABLE_ADDRESS_COMPARISONS, + unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS, unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS, unnecessary_sort_by::UNNECESSARY_SORT_BY, unnecessary_wraps::UNNECESSARY_WRAPS, diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index c2fc67afba5..ec187563b3f 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -20,6 +20,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(mutex_atomic::MUTEX_INTEGER), LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY), LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES), + LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION), LintId::of(option_if_let_else::OPTION_IF_LET_ELSE), LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE), LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE), @@ -27,6 +28,8 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(strings::STRING_LIT_AS_BYTES), LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY), + LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), + LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR), LintId::of(transmute::USELESS_TRANSMUTE), LintId::of(use_self::USE_SELF), diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs index eb6534cb8ca..2ee2c6e3358 100644 --- a/clippy_lints/src/lib.register_pedantic.rs +++ b/clippy_lints/src/lib.register_pedantic.rs @@ -82,14 +82,12 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(ref_option_ref::REF_OPTION_REF), LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE), LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED), + LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(strings::STRING_ADD_ASSIGN), - LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), - LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), LintId::of(transmute::TRANSMUTE_PTR_TO_PTR), LintId::of(types::LINKEDLIST), LintId::of(types::OPTION_OPTION), LintId::of(unicode::UNICODE_NOT_NFC), - LintId::of(unit_types::LET_UNIT_VALUE), LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS), LintId::of(unused_async::UNUSED_ASYNC), diff --git a/clippy_lints/src/lib.register_perf.rs b/clippy_lints/src/lib.register_perf.rs index f2f5c988d8f..82431863e6c 100644 --- a/clippy_lints/src/lib.register_perf.rs +++ b/clippy_lints/src/lib.register_perf.rs @@ -7,6 +7,7 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ LintId::of(escape::BOXED_LOCAL), LintId::of(format_args::FORMAT_IN_FORMAT_ARGS), LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS), + LintId::of(format_push_string::FORMAT_PUSH_STRING), LintId::of(large_const_arrays::LARGE_CONST_ARRAYS), LintId::of(large_enum_variant::LARGE_ENUM_VARIANT), LintId::of(loops::MANUAL_MEMCPY), @@ -23,7 +24,6 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ LintId::of(misc::CMP_OWNED), LintId::of(redundant_clone::REDUNDANT_CLONE), LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), - LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(types::BOX_COLLECTION), LintId::of(types::REDUNDANT_ALLOCATION), LintId::of(vec::USELESS_VEC), diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs index 4802dd877e9..77ec6c83ba4 100644 --- a/clippy_lints/src/lib.register_restriction.rs +++ b/clippy_lints/src/lib.register_restriction.rs @@ -16,6 +16,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(default_union_representation::DEFAULT_UNION_REPRESENTATION), LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS), LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE), + LintId::of(empty_drop::EMPTY_DROP), LintId::of(empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS), LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS), LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS), @@ -26,6 +27,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(indexing_slicing::INDEXING_SLICING), LintId::of(inherent_impl::MULTIPLE_INHERENT_IMPL), LintId::of(integer_division::INTEGER_DIVISION), + LintId::of(large_include_file::LARGE_INCLUDE_FILE), LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE), LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION), LintId::of(map_err_ignore::MAP_ERR_IGNORE), @@ -53,6 +55,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(panic_unimplemented::UNIMPLEMENTED), LintId::of(panic_unimplemented::UNREACHABLE), LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH), + LintId::of(pub_use::PUB_USE), LintId::of(redundant_slicing::DEREF_BY_SLICING), LintId::of(same_name_method::SAME_NAME_METHOD), LintId::of(shadow::SHADOW_REUSE), diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index 3114afac886..d183c0449cd 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -61,6 +61,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(methods::CHARS_NEXT_CMP), LintId::of(methods::ERR_EXPECT), LintId::of(methods::INTO_ITER_ON_REF), + LintId::of(methods::IS_DIGIT_ASCII_RADIX), LintId::of(methods::ITER_CLONED_COLLECT), LintId::of(methods::ITER_NEXT_SLICE), LintId::of(methods::ITER_NTH_ZERO), @@ -104,8 +105,11 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(returns::NEEDLESS_RETURN), LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS), LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), + LintId::of(strings::TRIM_SPLIT_WHITESPACE), LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME), + LintId::of(unit_types::LET_UNIT_VALUE), + LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS), LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(unused_unit::UNUSED_UNIT), LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS), diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs index 82f45b5fd58..7a881bfe399 100644 --- a/clippy_lints/src/lib.register_suspicious.rs +++ b/clippy_lints/src/lib.register_suspicious.rs @@ -5,6 +5,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec![ LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP), LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), + LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE), LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK), LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(casts::CAST_ABS_TO_UNSIGNED), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c9b836f9580..3bb821a1482 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -163,6 +163,8 @@ mod deprecated_lints; #[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))] mod utils; +mod renamed_lints; + // begin lints modules, do not remove this comment, it’s used in `update_lints` mod absurd_extreme_comparisons; mod approx_const; @@ -181,6 +183,7 @@ mod bool_assert_comparison; mod booleans; mod borrow_as_ptr; mod bytecount; +mod bytes_count_to_len; mod cargo; mod case_sensitive_file_extension_comparisons; mod casts; @@ -209,6 +212,7 @@ mod double_parens; mod drop_forget_ref; mod duration_subsec; mod else_if_without_else; +mod empty_drop; mod empty_enum; mod empty_structs_with_brackets; mod entry; @@ -231,6 +235,7 @@ mod floating_point_arithmetic; mod format; mod format_args; mod format_impl; +mod format_push_string; mod formatting; mod from_over_into; mod from_str_radix_10; @@ -259,6 +264,7 @@ mod items_after_statements; mod iter_not_returning_iterator; mod large_const_arrays; mod large_enum_variant; +mod large_include_file; mod large_stack_arrays; mod len_zero; mod let_if_seq; @@ -336,6 +342,7 @@ mod precedence; mod ptr; mod ptr_eq; mod ptr_offset_with_cast; +mod pub_use; mod question_mark; mod ranges; mod redundant_clone; @@ -383,6 +390,7 @@ mod unit_hash; mod unit_return_expecting_ord; mod unit_types; mod unnamed_address; +mod unnecessary_owned_empty_strings; mod unnecessary_self_imports; mod unnecessary_sort_by; mod unnecessary_wraps; @@ -499,7 +507,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: { store.register_early_pass(|| Box::new(utils::internal_lints::ClippyLintsInternal)); store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce)); - store.register_late_pass(|| Box::new(utils::inspector::DeepCodeInspector)); store.register_late_pass(|| Box::new(utils::internal_lints::CollapsibleCalls)); store.register_late_pass(|| Box::new(utils::internal_lints::CompilerLintFunctions::new())); store.register_late_pass(|| Box::new(utils::internal_lints::IfChainStyle)); @@ -511,8 +518,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(utils::internal_lints::MsrvAttrImpl)); } + store.register_late_pass(|| Box::new(utils::dump_hir::DumpHir)); store.register_late_pass(|| Box::new(utils::author::Author)); - store.register_late_pass(|| Box::new(await_holding_invalid::AwaitHolding)); + let await_holding_invalid_types = conf.await_holding_invalid_types.clone(); + store.register_late_pass(move || { + Box::new(await_holding_invalid::AwaitHolding::new( + await_holding_invalid_types.clone(), + )) + }); store.register_late_pass(|| Box::new(serde_api::SerdeApi)); let vec_box_size_threshold = conf.vec_box_size_threshold; let type_complexity_threshold = conf.type_complexity_threshold; @@ -572,7 +585,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(approx_const::ApproxConstant::new(msrv))); store.register_late_pass(move || Box::new(methods::Methods::new(avoid_breaking_exported_api, msrv))); store.register_late_pass(move || Box::new(matches::Matches::new(msrv))); - store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustive::new(msrv))); + store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv))); + store.register_late_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv))); store.register_late_pass(move || Box::new(manual_strip::ManualStrip::new(msrv))); store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv))); store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv))); @@ -812,6 +826,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(disallowed_methods::DisallowedMethods::new(disallowed_methods.clone()))); store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax)); store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax)); + store.register_late_pass(|| Box::new(empty_drop::EmptyDrop)); store.register_late_pass(|| Box::new(strings::StrToString)); store.register_late_pass(|| Box::new(strings::StringToString)); store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues)); @@ -868,6 +883,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: }); store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef)); store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets)); + store.register_late_pass(|| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings)); + store.register_early_pass(|| Box::new(pub_use::PubUse)); + store.register_late_pass(|| Box::new(format_push_string::FormatPushString)); + store.register_late_pass(|| Box::new(bytes_count_to_len::BytesCountToLen)); + let max_include_file_size = conf.max_include_file_size; + store.register_late_pass(move || Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size))); + store.register_late_pass(|| Box::new(strings::TrimSplitWhitespace)); // add lints here, do not remove this comment, it's used in `new_lint` } @@ -919,43 +941,9 @@ fn register_removed_non_tool_lints(store: &mut rustc_lint::LintStore) { /// /// Used in `./src/driver.rs`. pub fn register_renamed(ls: &mut rustc_lint::LintStore) { - // NOTE: when renaming a lint, add a corresponding test to tests/ui/rename.rs - ls.register_renamed("clippy::stutter", "clippy::module_name_repetitions"); - ls.register_renamed("clippy::new_without_default_derive", "clippy::new_without_default"); - ls.register_renamed("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"); - ls.register_renamed("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"); - ls.register_renamed("clippy::option_and_then_some", "clippy::bind_instead_of_map"); - ls.register_renamed("clippy::box_vec", "clippy::box_collection"); - ls.register_renamed("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions"); - ls.register_renamed("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions"); - ls.register_renamed("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"); - ls.register_renamed("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"); - ls.register_renamed("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"); - ls.register_renamed("clippy::option_unwrap_used", "clippy::unwrap_used"); - ls.register_renamed("clippy::result_unwrap_used", "clippy::unwrap_used"); - ls.register_renamed("clippy::option_expect_used", "clippy::expect_used"); - ls.register_renamed("clippy::result_expect_used", "clippy::expect_used"); - ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"); - ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"); - ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion"); - ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters"); - ls.register_renamed("clippy::single_char_push_str", "clippy::single_char_add_str"); - ls.register_renamed("clippy::if_let_some_result", "clippy::match_result_ok"); - ls.register_renamed("clippy::disallowed_type", "clippy::disallowed_types"); - ls.register_renamed("clippy::disallowed_method", "clippy::disallowed_methods"); - ls.register_renamed("clippy::ref_in_deref", "clippy::needless_borrow"); - ls.register_renamed("clippy::to_string_in_display", "clippy::recursive_format_impl"); - - // uplifted lints - ls.register_renamed("clippy::invalid_ref", "invalid_value"); - ls.register_renamed("clippy::into_iter_on_array", "array_into_iter"); - ls.register_renamed("clippy::unused_label", "unused_labels"); - ls.register_renamed("clippy::drop_bounds", "drop_bounds"); - ls.register_renamed("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"); - ls.register_renamed("clippy::panic_params", "non_fmt_panics"); - ls.register_renamed("clippy::unknown_clippy_lints", "unknown_lints"); - ls.register_renamed("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"); - ls.register_renamed("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"); + for (old_name, new_name) in renamed_lints::RENAMED_LINTS { + ls.register_renamed(old_name, new_name); + } } // only exists to let the dogfood integration test works. diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 662a561f171..ab5d3fa7b6d 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -1,16 +1,19 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::trait_ref_of_method; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter}; use rustc_hir::intravisit::{ - walk_fn_decl, walk_generic_param, walk_generics, walk_item, walk_param_bound, walk_poly_trait_ref, walk_ty, Visitor, + walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound, + walk_poly_trait_ref, walk_trait_ref, walk_ty, Visitor, }; use rustc_hir::FnRetTy::Return; use rustc_hir::{ - BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, + BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, TraitBoundModifier, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::nested_filter as middle_nested_filter; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::{kw, Ident, Symbol}; @@ -82,8 +85,10 @@ declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]); impl<'tcx> LateLintPass<'tcx> for Lifetimes { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if let ItemKind::Fn(ref sig, ref generics, id) = item.kind { + if let ItemKind::Fn(ref sig, generics, id) = item.kind { check_fn_inner(cx, sig.decl, Some(id), None, generics, item.span, true); + } else if let ItemKind::Impl(impl_) = item.kind { + report_extra_impl_lifetimes(cx, impl_); } } @@ -95,7 +100,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { sig.decl, Some(id), None, - &item.generics, + item.generics, item.span, report_extra_lifetimes, ); @@ -108,7 +113,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { TraitFn::Required(sig) => (None, Some(sig)), TraitFn::Provided(id) => (Some(id), None), }; - check_fn_inner(cx, sig.decl, body, trait_sig, &item.generics, item.span, true); + check_fn_inner(cx, sig.decl, body, trait_sig, item.generics, item.span, true); } } } @@ -201,8 +206,7 @@ fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: visitor.visit_ty(self_ty); !visitor.all_lts().is_empty() - } - else { + } else { false } } @@ -486,11 +490,29 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_ false } -struct LifetimeChecker { +struct LifetimeChecker<'cx, 'tcx, F> { + cx: &'cx LateContext<'tcx>, map: FxHashMap, + phantom: std::marker::PhantomData, } -impl<'tcx> Visitor<'tcx> for LifetimeChecker { +impl<'cx, 'tcx, F> LifetimeChecker<'cx, 'tcx, F> { + fn new(cx: &'cx LateContext<'tcx>, map: FxHashMap) -> LifetimeChecker<'cx, 'tcx, F> { + Self { + cx, + map, + phantom: std::marker::PhantomData, + } + } +} + +impl<'cx, 'tcx, F> Visitor<'tcx> for LifetimeChecker<'cx, 'tcx, F> +where + F: NestedFilter<'tcx>, +{ + type Map = rustc_middle::hir::map::Map<'tcx>; + type NestedFilter = F; + // for lifetimes as parameters of generics fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) { self.map.remove(&lifetime.name.ident().name); @@ -506,6 +528,10 @@ impl<'tcx> Visitor<'tcx> for LifetimeChecker { walk_generic_param(self, param); } } + + fn nested_visit_map(&mut self) -> Self::Map { + self.cx.tcx.hir() + } } fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, generics: &'tcx Generics<'_>) { @@ -517,7 +543,7 @@ fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, _ => None, }) .collect(); - let mut checker = LifetimeChecker { map: hs }; + let mut checker = LifetimeChecker::::new(cx, hs); walk_generics(&mut checker, generics); walk_fn_decl(&mut checker, func); @@ -532,6 +558,32 @@ fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, } } +fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<'_>) { + let hs = impl_ + .generics + .params + .iter() + .filter_map(|par| match par.kind { + GenericParamKind::Lifetime { .. } => Some((par.name.ident().name, par.span)), + _ => None, + }) + .collect(); + let mut checker = LifetimeChecker::::new(cx, hs); + + walk_generics(&mut checker, impl_.generics); + if let Some(ref trait_ref) = impl_.of_trait { + walk_trait_ref(&mut checker, trait_ref); + } + walk_ty(&mut checker, impl_.self_ty); + for item in impl_.items { + walk_impl_item_ref(&mut checker, item); + } + + for &v in checker.map.values() { + span_lint(cx, EXTRA_UNUSED_LIFETIMES, v, "this lifetime isn't used in the impl"); + } +} + struct BodyLifetimeChecker { lifetimes_used_in_body: bool, } diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index b7430f49229..269d3c62eaf 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -42,17 +42,12 @@ declare_clippy_lint! { /// This is most probably a typo /// /// ### Known problems - /// - Recommends a signed suffix, even though the number might be too big and an unsigned - /// suffix is required + /// - Does not match on integers too large to fit in the corresponding unsigned type /// - Does not match on `_127` since that is a valid grouping for decimal and octal numbers /// /// ### Example - /// ```rust - /// // Probably mistyped - /// 2_32; - /// - /// // Good - /// 2_i32; + /// `2_32` => `2_i32` + /// `250_8 => `250_u8` /// ``` #[clippy::version = "1.30.0"] pub MISTYPED_LITERAL_SUFFIXES, @@ -310,18 +305,47 @@ impl LiteralDigitGrouping { return true; } - let (part, mistyped_suffixes, missing_char) = if let Some((_, exponent)) = &mut num_lit.exponent { - (exponent, &["32", "64"][..], 'f') + let (part, mistyped_suffixes, is_float) = if let Some((_, exponent)) = &mut num_lit.exponent { + (exponent, &["32", "64"][..], true) } else if num_lit.fraction.is_some() { - (&mut num_lit.integer, &["32", "64"][..], 'f') + return true; } else { - (&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i') + (&mut num_lit.integer, &["8", "16", "32", "64"][..], false) }; let mut split = part.rsplit('_'); let last_group = split.next().expect("At least one group"); if split.next().is_some() && mistyped_suffixes.contains(&last_group) { - *part = &part[..part.len() - last_group.len()]; + let main_part = &part[..part.len() - last_group.len()]; + let missing_char; + if is_float { + missing_char = 'f'; + } else { + let radix = match num_lit.radix { + Radix::Binary => 2, + Radix::Octal => 8, + Radix::Decimal => 10, + Radix::Hexadecimal => 16, + }; + if let Ok(int) = u64::from_str_radix(&main_part.replace('_', ""), radix) { + missing_char = match (last_group, int) { + ("8", i) if i8::try_from(i).is_ok() => 'i', + ("16", i) if i16::try_from(i).is_ok() => 'i', + ("32", i) if i32::try_from(i).is_ok() => 'i', + ("64", i) if i64::try_from(i).is_ok() => 'i', + ("8", u) if u8::try_from(u).is_ok() => 'u', + ("16", u) if u16::try_from(u).is_ok() => 'u', + ("32", u) if u32::try_from(u).is_ok() => 'u', + ("64", _) => 'u', + _ => { + return true; + }, + } + } else { + return true; + } + } + *part = main_part; let mut sugg = num_lit.format(); sugg.push('_'); sugg.push(missing_char); diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 9ba9642fcc8..70a118d6b35 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -168,8 +168,9 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { .operands .iter() .map(|(o, _)| match o { - InlineAsmOperand::In { expr, .. } - | InlineAsmOperand::InOut { expr, .. } => never_loop_expr(expr, main_loop_id), + InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => { + never_loop_expr(expr, main_loop_id) + }, InlineAsmOperand::Out { expr, .. } => never_loop_expr_all(&mut expr.iter(), main_loop_id), InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { never_loop_expr_all(&mut once(in_expr).chain(out_expr.iter()), main_loop_id) diff --git a/clippy_lints/src/manual_bits.rs b/clippy_lints/src/manual_bits.rs index dcf44303cf4..ac3d9447b6b 100644 --- a/clippy_lints/src/manual_bits.rs +++ b/clippy_lints/src/manual_bits.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_opt; -use clippy_utils::{meets_msrv, msrvs}; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{get_parent_expr, meets_msrv, msrvs}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath}; @@ -24,7 +24,7 @@ declare_clippy_lint! { /// ``` /// Use instead: /// ```rust - /// usize::BITS; + /// usize::BITS as usize; /// ``` #[clippy::version = "1.60.0"] pub MANUAL_BITS, @@ -59,16 +59,19 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits { if matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_)); if let ExprKind::Lit(lit) = &other_expr.kind; if let LitKind::Int(8, _) = lit.node; - then { + let mut app = Applicability::MachineApplicable; + let ty_snip = snippet_with_applicability(cx, real_ty.span, "..", &mut app); + let sugg = create_sugg(cx, expr, format!("{ty_snip}::BITS")); + span_lint_and_sugg( cx, MANUAL_BITS, expr.span, "usage of `mem::size_of::()` to obtain the size of `T` in bits", "consider using", - format!("{}::BITS", snippet_opt(cx, real_ty.span).unwrap()), - Applicability::MachineApplicable, + sugg, + app, ); } } @@ -108,3 +111,36 @@ fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option< } } } + +fn create_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, base_sugg: String) -> String { + if let Some(parent_expr) = get_parent_expr(cx, expr) { + if is_ty_conversion(parent_expr) { + return base_sugg; + } + + // These expressions have precedence over casts, the suggestion therefore + // needs to be wrapped into parentheses + match parent_expr.kind { + ExprKind::Unary(..) | ExprKind::AddrOf(..) | ExprKind::MethodCall(..) => { + return format!("({base_sugg} as usize)"); + }, + _ => {}, + } + } + + format!("{base_sugg} as usize") +} + +fn is_ty_conversion(expr: &Expr<'_>) -> bool { + if let ExprKind::Cast(..) = expr.kind { + true + } else if let ExprKind::MethodCall(path, [_], _) = expr.kind + && path.ident.name == rustc_span::sym::try_into + { + // This is only called for `usize` which implements `TryInto`. Therefore, + // we don't have to check here if `self` implements the `TryInto` trait. + true + } else { + false + } +} diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 33d1bb2985f..b8d620d8171 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -1,13 +1,17 @@ use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint_and_then; 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 clippy_utils::{is_lint_allowed, meets_msrv, msrvs}; +use rustc_ast::ast::{self, VisibilityKind}; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; +use rustc_hir::{self as hir, Expr, ExprKind, QPath}; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; +use rustc_middle::ty::DefIdTree; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::{sym, Span}; declare_clippy_lint! { @@ -58,129 +62,160 @@ declare_clippy_lint! { "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]" } -#[derive(Clone)] -pub struct ManualNonExhaustive { +#[allow(clippy::module_name_repetitions)] +pub struct ManualNonExhaustiveStruct { msrv: Option, } -impl ManualNonExhaustive { +impl ManualNonExhaustiveStruct { #[must_use] pub fn new(msrv: Option) -> Self { Self { msrv } } } -impl_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]); +impl_lint_pass!(ManualNonExhaustiveStruct => [MANUAL_NON_EXHAUSTIVE]); -impl EarlyLintPass for ManualNonExhaustive { - fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { +#[allow(clippy::module_name_repetitions)] +pub struct ManualNonExhaustiveEnum { + msrv: Option, + constructed_enum_variants: FxHashSet<(DefId, DefId)>, + potential_enums: Vec<(LocalDefId, LocalDefId, Span, Span)>, +} + +impl ManualNonExhaustiveEnum { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { + msrv, + constructed_enum_variants: FxHashSet::default(), + potential_enums: Vec::new(), + } + } +} + +impl_lint_pass!(ManualNonExhaustiveEnum => [MANUAL_NON_EXHAUSTIVE]); + +impl EarlyLintPass for ManualNonExhaustiveStruct { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) { return; } - match &item.kind { - ItemKind::Enum(def, _) => { - check_manual_non_exhaustive_enum(cx, item, &def.variants); - }, - ItemKind::Struct(variant_data, _) => { - if let VariantData::Unit(..) = variant_data { - return; - } - - check_manual_non_exhaustive_struct(cx, item, variant_data); - }, - _ => {}, + if let ast::ItemKind::Struct(variant_data, _) = &item.kind { + let (fields, delimiter) = match variant_data { + ast::VariantData::Struct(fields, _) => (&**fields, '{'), + ast::VariantData::Tuple(fields, _) => (&**fields, '('), + ast::VariantData::Unit(_) => return, + }; + if fields.len() <= 1 { + return; + } + let mut iter = fields.iter().filter_map(|f| match f.vis.kind { + VisibilityKind::Public => None, + VisibilityKind::Inherited => Some(Ok(f)), + _ => Some(Err(())), + }); + if let Some(Ok(field)) = iter.next() + && iter.next().is_none() + && field.ty.kind.is_unit() + && field.ident.map_or(true, |name| name.as_str().starts_with('_')) + { + span_lint_and_then( + cx, + MANUAL_NON_EXHAUSTIVE, + item.span, + "this seems like a manual implementation of the non-exhaustive pattern", + |diag| { + if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive)) + && let header_span = cx.sess().source_map().span_until_char(item.span, delimiter) + && let Some(snippet) = snippet_opt(cx, header_span) + { + diag.span_suggestion( + header_span, + "add the attribute", + format!("#[non_exhaustive] {}", snippet), + Applicability::Unspecified, + ); + } + diag.span_help(field.span, "remove this field"); + } + ); + } } } extract_msrv_attr!(EarlyContext); } -fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants: &[Variant]) { - fn is_non_exhaustive_marker(variant: &Variant) -> bool { - matches!(variant.data, VariantData::Unit(_)) - && variant.ident.as_str().starts_with('_') - && is_doc_hidden(&variant.attrs) +impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { + if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) { + return; + } + + if let hir::ItemKind::Enum(def, _) = &item.kind + && def.variants.len() > 1 + { + let mut iter = def.variants.iter().filter_map(|v| { + let id = cx.tcx.hir().local_def_id(v.id); + (matches!(v.data, hir::VariantData::Unit(_)) + && v.ident.as_str().starts_with('_') + && is_doc_hidden(cx.tcx.get_attrs(id.to_def_id()))) + .then(|| (id, v.span)) + }); + if let Some((id, span)) = iter.next() + && iter.next().is_none() + { + self.potential_enums.push((item.def_id, id, item.span, span)); + } + } } - let mut markers = variants.iter().filter(|v| is_non_exhaustive_marker(v)); - if_chain! { - if let Some(marker) = markers.next(); - if markers.count() == 0 && variants.len() > 1; - then { + fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { + if let ExprKind::Path(QPath::Resolved(None, p)) = &e.kind + && let [.., name] = p.segments + && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res + && name.ident.as_str().starts_with('_') + { + let variant_id = cx.tcx.parent(id); + let enum_id = cx.tcx.parent(variant_id); + + self.constructed_enum_variants.insert((enum_id, variant_id)); + } + } + + fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { + for &(enum_id, _, enum_span, variant_span) in + self.potential_enums.iter().filter(|&&(enum_id, variant_id, _, _)| { + !self + .constructed_enum_variants + .contains(&(enum_id.to_def_id(), variant_id.to_def_id())) + && !is_lint_allowed(cx, MANUAL_NON_EXHAUSTIVE, cx.tcx.hir().local_def_id_to_hir_id(enum_id)) + }) + { span_lint_and_then( cx, MANUAL_NON_EXHAUSTIVE, - item.span, + enum_span, "this seems like a manual implementation of the non-exhaustive pattern", |diag| { - if_chain! { - if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive)); - let header_span = cx.sess().source_map().span_until_char(item.span, '{'); - if let Some(snippet) = snippet_opt(cx, header_span); - then { + if !cx.tcx.adt_def(enum_id).is_variant_list_non_exhaustive() + && let header_span = cx.sess().source_map().span_until_char(enum_span, '{') + && let Some(snippet) = snippet_opt(cx, header_span) + { diag.span_suggestion( header_span, "add the attribute", format!("#[non_exhaustive] {}", snippet), Applicability::Unspecified, ); - } } - diag.span_help(marker.span, "remove this variant"); - }); - } - } -} - -fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) { - fn is_private(field: &FieldDef) -> bool { - matches!(field.vis.kind, VisibilityKind::Inherited) - } - - fn is_non_exhaustive_marker(field: &FieldDef) -> bool { - is_private(field) && field.ty.kind.is_unit() && field.ident.map_or(true, |n| n.as_str().starts_with('_')) - } - - fn find_header_span(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) -> Span { - let delimiter = match data { - VariantData::Struct(..) => '{', - VariantData::Tuple(..) => '(', - VariantData::Unit(_) => unreachable!("`VariantData::Unit` is already handled above"), - }; - - cx.sess().source_map().span_until_char(item.span, delimiter) - } - - let fields = data.fields(); - let private_fields = fields.iter().filter(|f| is_private(f)).count(); - let public_fields = fields.iter().filter(|f| f.vis.kind.is_pub()).count(); - - if_chain! { - if private_fields == 1 && public_fields >= 1 && public_fields == fields.len() - 1; - if let Some(marker) = fields.iter().find(|f| is_non_exhaustive_marker(f)); - then { - span_lint_and_then( - cx, - MANUAL_NON_EXHAUSTIVE, - item.span, - "this seems like a manual implementation of the non-exhaustive pattern", - |diag| { - if_chain! { - if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive)); - let header_span = find_header_span(cx, item, data); - if let Some(snippet) = snippet_opt(cx, header_span); - then { - diag.span_suggestion( - header_span, - "add the attribute", - format!("#[non_exhaustive] {}", snippet), - Applicability::Unspecified, - ); - } - } - diag.span_help(marker.span, "remove this field"); - }); + diag.span_help(variant_span, "remove this variant"); + }, + ); } } + + extract_msrv_attr!(LateContext); } diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index e233300e26a..ceb66947d02 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -143,15 +143,11 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) { impl MapClone { fn lint_explicit_closure(&self, cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool) { let mut applicability = Applicability::MachineApplicable; - let message = if is_copy { - "you are using an explicit closure for copying elements" + + let (message, sugg_method) = if is_copy && meets_msrv(self.msrv.as_ref(), &msrvs::ITERATOR_COPIED) { + ("you are using an explicit closure for copying elements", "copied") } else { - "you are using an explicit closure for cloning elements" - }; - let sugg_method = if is_copy && meets_msrv(self.msrv.as_ref(), &msrvs::ITERATOR_COPIED) { - "copied" - } else { - "cloned" + ("you are using an explicit closure for cloning elements", "cloned") }; span_lint_and_sugg( diff --git a/clippy_lints/src/match_result_ok.rs b/clippy_lints/src/match_result_ok.rs index 77a4917ec58..3349b85f134 100644 --- a/clippy_lints/src/match_result_ok.rs +++ b/clippy_lints/src/match_result_ok.rs @@ -24,7 +24,7 @@ declare_clippy_lint! { /// vec.push(value) /// } /// - /// if let Some(valie) = iter.next().ok() { + /// if let Some(value) = iter.next().ok() { /// vec.push(value) /// } /// ``` @@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk { if_chain! { if let ExprKind::MethodCall(ok_path, [ref result_types_0, ..], _) = let_expr.kind; //check is expr.ok() has type Result.ok(, _) if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _) = let_pat.kind; //get operation - if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized; + if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() method use std::marker::Sized; if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(result_types_0), sym::Result); if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some"; diff --git a/clippy_lints/src/matches/infalliable_detructuring_match.rs b/clippy_lints/src/matches/infallible_destructuring_match.rs similarity index 100% rename from clippy_lints/src/matches/infalliable_detructuring_match.rs rename to clippy_lints/src/matches/infallible_destructuring_match.rs diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index b8591fe0db0..9b7344fb8b0 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -30,7 +30,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { .map(|a| NormalizedPat::from_pat(cx, &arena, a.pat)) .collect(); - // The furthast forwards a pattern can move without semantic changes + // The furthest forwards a pattern can move without semantic changes let forwards_blocking_idxs: Vec<_> = normalized_pats .iter() .enumerate() @@ -43,7 +43,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { }) .collect(); - // The furthast backwards a pattern can move without semantic changes + // The furthest backwards a pattern can move without semantic changes let backwards_blocking_idxs: Vec<_> = normalized_pats .iter() .enumerate() diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index e93b494653f..401ecef460c 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -1,4 +1,4 @@ -use clippy_utils::source::{snippet_opt, walk_span_to_context}; +use clippy_utils::source::{snippet_opt, span_starts_with, walk_span_to_context}; use clippy_utils::{meets_msrv, msrvs}; use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat}; use rustc_lexer::{tokenize, TokenKind}; @@ -7,7 +7,7 @@ use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{Span, SpanData, SyntaxContext}; -mod infalliable_detructuring_match; +mod infallible_destructuring_match; mod match_as_ref; mod match_bool; mod match_like_matches; @@ -653,6 +653,9 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } if let ExprKind::Match(ex, arms, source) = expr.kind { + if !span_starts_with(cx, expr.span, "match") { + return; + } if !contains_cfg_arm(cx, expr, ex, arms) { if source == MatchSource::Normal { if !(meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO) @@ -691,7 +694,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) { - self.infallible_destructuring_match_linted |= infalliable_detructuring_match::check(cx, local); + self.infallible_destructuring_match_linted |= infallible_destructuring_match::check(cx, local); } fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index 2105a03e03a..f920ad4651f 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -83,7 +83,7 @@ fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool { return false; } - // Recurrsively check for each `else if let` phrase, + // Recursively check for each `else if let` phrase, if let Some(ref nested_if_let) = higher::IfLet::hir(cx, if_else) { return check_if_let(cx, nested_if_let); } @@ -99,7 +99,7 @@ fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool { if let ExprKind::Path(ref qpath) = ret.kind { return is_lang_ctor(cx, qpath, OptionNone) || eq_expr_value(cx, if_let.let_expr, ret); } - return true; + return false; } return eq_expr_value(cx, if_let.let_expr, ret); } @@ -118,7 +118,7 @@ fn strip_return<'hir>(expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> { } /// Manually check for coercion casting by checking if the type of the match operand or let expr -/// differs with the assigned local variable or the funtion return type. +/// differs with the assigned local variable or the function return type. fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_>) -> bool { if let Some(p_node) = get_parent_node(cx.tcx, p_expr.hir_id) { match p_node { diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index aa3552001f4..37b67647efe 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -2,17 +2,16 @@ use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type}; +use clippy_utils::ty::needs_ordered_drop; use clippy_utils::{higher, match_def_path}; use clippy_utils::{is_lang_ctor, 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, PollPending}; use rustc_hir::{ intravisit::{walk_expr, Visitor}, - Arm, Block, Expr, ExprKind, LangItem, Node, Pat, PatKind, QPath, UnOp, + Arm, Block, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp, }; use rustc_lint::LateContext; use rustc_middle::ty::{self, subst::GenericArgKind, DefIdTree, Ty}; @@ -32,59 +31,6 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { } } -/// 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<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - type_needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default()) -} - -fn type_needs_ordered_drop_inner<'tcx>(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 - .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(fields) => fields.iter().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)), - ty::Array(ty, _) => type_needs_ordered_drop_inner(cx, *ty, seen), - ty::Adt(adt, subs) => adt - .all_fields() - .map(|f| f.ty(cx.tcx, subs)) - .any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)), - _ => true, - } - } - // Check for std types which implement drop, but only for memory allocation. - else if is_type_diagnostic_item(cx, ty, sym::Vec) - || is_type_lang_item(cx, ty, LangItem::OwnedBox) - || is_type_diagnostic_item(cx, ty, sym::Rc) - || is_type_diagnostic_item(cx, ty, sym::Arc) - || is_type_diagnostic_item(cx, ty, sym::cstring_type) - || is_type_diagnostic_item(cx, ty, sym::BTreeMap) - || is_type_diagnostic_item(cx, ty, sym::LinkedList) - || match_type(cx, ty, &paths::WEAK_RC) - || match_type(cx, ty, &paths::WEAK_ARC) - { - // Check all of the generic arguments. - if let ty::Adt(_, subs) = ty.kind() { - subs.types().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)) - } else { - true - } - } else { - true - } -} - // Extract the generic arguments out of a type fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option> { if_chain! { @@ -115,7 +61,7 @@ fn temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr< // 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)) { + if needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(expr)) { self.res = true; } else { self.visit_expr(expr); @@ -126,7 +72,7 @@ fn temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr< // 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)) { + if needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(base)) { self.res = true; } else { self.visit_expr(base); @@ -143,7 +89,7 @@ fn temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr< .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)) { + if self_by_ref && needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(self_arg)) { self.res = true; } else { self.visit_expr(self_arg); @@ -243,7 +189,7 @@ fn find_sugg_for_if_let<'tcx>( // scrutinee would be, so they have to be considered as well. // e.g. in `if let Some(x) = foo.lock().unwrap().baz.as_ref() { .. }` the lock will be held // for the duration if body. - let needs_drop = type_needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, let_expr); + let needs_drop = needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, let_expr); // check that `while_let_on_iterator` lint does not trigger if_chain! { diff --git a/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs b/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs index 5076239a57c..0aadb482acd 100644 --- a/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs +++ b/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs @@ -14,6 +14,7 @@ pub(crate) fn check(cx: &LateContext<'_>, pat: &Pat<'_>) { if let ty::Adt(def, _) = ty.kind(); if def.is_struct() || def.is_union(); if fields.len() == def.non_enum_variant().fields.len(); + if !def.non_enum_variant().is_field_list_non_exhaustive(); then { span_lint_and_help( diff --git a/clippy_lints/src/methods/is_digit_ascii_radix.rs b/clippy_lints/src/methods/is_digit_ascii_radix.rs new file mode 100644 index 00000000000..ad333df2f2d --- /dev/null +++ b/clippy_lints/src/methods/is_digit_ascii_radix.rs @@ -0,0 +1,50 @@ +//! Lint for `c.is_digit(10)` + +use super::IS_DIGIT_ASCII_RADIX; +use clippy_utils::{ + consts::constant_full_int, consts::FullInt, diagnostics::span_lint_and_sugg, meets_msrv, msrvs, + source::snippet_with_applicability, +}; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_semver::RustcVersion; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + self_arg: &'tcx Expr<'_>, + radix: &'tcx Expr<'_>, + msrv: Option<&RustcVersion>, +) { + if !meets_msrv(msrv, &msrvs::IS_ASCII_DIGIT) { + return; + } + + if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_char() { + return; + } + + if let Some(radix_val) = constant_full_int(cx, cx.typeck_results(), radix) { + let (num, replacement) = match radix_val { + FullInt::S(10) | FullInt::U(10) => (10, "is_ascii_digit"), + FullInt::S(16) | FullInt::U(16) => (16, "is_ascii_hexdigit"), + _ => return, + }; + let mut applicability = Applicability::MachineApplicable; + + span_lint_and_sugg( + cx, + IS_DIGIT_ASCII_RADIX, + expr.span, + &format!("use of `char::is_digit` with literal radix of {}", num), + "try", + format!( + "{}.{}()", + snippet_with_applicability(cx, self_arg.span, "..", &mut applicability), + replacement + ), + applicability, + ); + } +} diff --git a/clippy_lints/src/methods/iter_with_drain.rs b/clippy_lints/src/methods/iter_with_drain.rs index 958c3773087..152072e09c7 100644 --- a/clippy_lints/src/methods/iter_with_drain.rs +++ b/clippy_lints/src/methods/iter_with_drain.rs @@ -1,72 +1,47 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::higher::Range; use clippy_utils::is_integer_const; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{ - higher::{self, Range}, - SpanlessEq, -}; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::sym; use rustc_span::Span; use super::ITER_WITH_DRAIN; -const DRAIN_TYPES: &[Symbol] = &[sym::Vec, sym::VecDeque]; - pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, arg: &Expr<'_>) { - let ty = cx.typeck_results().expr_ty(recv).peel_refs(); - if let Some(drained_type) = DRAIN_TYPES.iter().find(|&&sym| is_type_diagnostic_item(cx, ty, sym)) { - // Refuse to emit `into_iter` suggestion on draining struct fields due - // to the strong possibility of processing unmovable field. - if let ExprKind::Field(..) = recv.kind { - return; - } - - if let Some(range) = higher::Range::hir(arg) { - let left_full = match range { - Range { start: Some(start), .. } if is_integer_const(cx, start, 0) => true, - Range { start: None, .. } => true, - _ => false, - }; - let full = left_full - && match range { - Range { - end: Some(end), - limits: RangeLimits::HalfOpen, - .. - } => { - // `x.drain(..x.len())` call - if_chain! { - if let ExprKind::MethodCall(len_path, len_args, _) = end.kind; - if len_path.ident.name == sym::len && len_args.len() == 1; - if let ExprKind::Path(QPath::Resolved(_, drain_path)) = recv.kind; - if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_args[0].kind; - if SpanlessEq::new(cx).eq_path(drain_path, len_path); - then { true } - else { false } - } - }, - Range { - end: None, - limits: RangeLimits::HalfOpen, - .. - } => true, - _ => false, - }; - if full { - span_lint_and_sugg( - cx, - ITER_WITH_DRAIN, - span.with_hi(expr.span.hi()), - &format!("`drain(..)` used on a `{}`", drained_type), - "try this", - "into_iter()".to_string(), - Applicability::MaybeIncorrect, - ); - } - } - } + if !matches!(recv.kind, ExprKind::Field(..)) + && let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def() + && let Some(ty_name) = cx.tcx.get_diagnostic_name(adt.did()) + && matches!(ty_name, sym::Vec | sym::VecDeque) + && let Some(range) = Range::hir(arg) + && is_full_range(cx, recv, range) + { + span_lint_and_sugg( + cx, + ITER_WITH_DRAIN, + span.with_hi(expr.span.hi()), + &format!("`drain(..)` used on a `{}`", ty_name), + "try this", + "into_iter()".to_string(), + Applicability::MaybeIncorrect, + ); + }; +} + +fn is_full_range(cx: &LateContext<'_>, container: &Expr<'_>, range: Range<'_>) -> bool { + range.start.map_or(true, |e| is_integer_const(cx, e, 0)) + && range.end.map_or(true, |e| { + if range.limits == RangeLimits::HalfOpen + && let ExprKind::Path(QPath::Resolved(None, container_path)) = container.kind + && let ExprKind::MethodCall(name, [self_arg], _) = e.kind + && name.ident.name == sym::len + && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind + { + container_path.res == path.res + } else { + false + } + }) } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 70d021a1668..f3be71f6b8b 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -26,6 +26,7 @@ mod implicit_clone; mod inefficient_to_string; mod inspect_for_each; mod into_iter_on_ref; +mod is_digit_ascii_radix; mod iter_cloned_collect; mod iter_count; mod iter_next_slice; @@ -42,6 +43,7 @@ mod map_flatten; mod map_identity; mod map_unwrap_or; mod needless_option_as_deref; +mod needless_option_take; mod ok_expect; mod option_as_ref_deref; mod option_map_or_none; @@ -294,15 +296,15 @@ declare_clippy_lint! { /// Checks for methods with certain name prefixes and which /// doesn't match how self is taken. The actual rules are: /// - /// |Prefix |Postfix |`self` taken | `self` type | - /// |-------|------------|-----------------------|--------------| - /// |`as_` | none |`&self` or `&mut self` | any | - /// |`from_`| none | none | any | - /// |`into_`| none |`self` | any | - /// |`is_` | none |`&self` or none | any | - /// |`to_` | `_mut` |`&mut self` | any | - /// |`to_` | not `_mut` |`self` | `Copy` | - /// |`to_` | not `_mut` |`&self` | not `Copy` | + /// |Prefix |Postfix |`self` taken | `self` type | + /// |-------|------------|-------------------------------|--------------| + /// |`as_` | none |`&self` or `&mut self` | any | + /// |`from_`| none | none | any | + /// |`into_`| none |`self` | any | + /// |`is_` | none |`&mut self` or `&self` or none | any | + /// |`to_` | `_mut` |`&mut self` | any | + /// |`to_` | not `_mut` |`self` | `Copy` | + /// |`to_` | not `_mut` |`&self` | not `Copy` | /// /// Note: Clippy doesn't trigger methods with `to_` prefix in: /// - Traits definition. @@ -1265,7 +1267,7 @@ declare_clippy_lint! { #[clippy::version = "1.55.0"] pub EXTEND_WITH_DRAIN, perf, - "using vec.append(&mut vec) to move the full range of a vecor to another" + "using vec.append(&mut vec) to move the full range of a vector to another" } declare_clippy_lint! { @@ -2007,13 +2009,27 @@ declare_clippy_lint! { /// ### Example /// ```rust,ignore /// // Bad - /// let (key, value) = _.splitn(2, '=').next_tuple()?; - /// let value = _.splitn(2, '=').nth(1)?; + /// let s = "key=value=add"; + /// let (key, value) = s.splitn(2, '=').next_tuple()?; + /// let value = s.splitn(2, '=').nth(1)?; /// - /// // Good - /// let (key, value) = _.split_once('=')?; - /// let value = _.split_once('=')?.1; + /// let mut parts = s.splitn(2, '='); + /// let key = parts.next()?; + /// let value = parts.next()?; /// ``` + /// Use instead: + /// ```rust,ignore + /// // Good + /// let s = "key=value=add"; + /// let (key, value) = s.split_once('=')?; + /// let value = s.split_once('=')?.1; + /// + /// let (key, value) = s.split_once('=')?; + /// ``` + /// + /// ### Limitations + /// The multiple statement variant currently only detects `iter.next()?`/`iter.next().unwrap()` + /// in two separate `let` statements that immediately follow the `splitn()` #[clippy::version = "1.57.0"] pub MANUAL_SPLIT_ONCE, complexity, @@ -2099,7 +2115,7 @@ declare_clippy_lint! { /// using `.collect::()` over `.collect::>().join("")` /// will prevent loop unrolling and will result in a negative performance impact. /// - /// Additionlly, differences have been observed between aarch64 and x86_64 assembly output, + /// Additionally, differences have been observed between aarch64 and x86_64 assembly output, /// with aarch64 tending to producing faster assembly in more cases when using `.collect::()` #[clippy::version = "1.61.0"] pub UNNECESSARY_JOIN, @@ -2131,6 +2147,56 @@ declare_clippy_lint! { "no-op use of `deref` or `deref_mut` method to `Option`." } +declare_clippy_lint! { + /// ### What it does + /// Finds usages of [`char::is_digit`] + /// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_digit) that + /// can be replaced with [`is_ascii_digit`] + /// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_digit) or + /// [`is_ascii_hexdigit`] + /// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_hexdigit). + /// + /// ### Why is this bad? + /// `is_digit(..)` is slower and requires specifying the radix. + /// + /// ### Example + /// ```rust + /// let c: char = '6'; + /// c.is_digit(10); + /// c.is_digit(16); + /// ``` + /// Use instead: + /// ```rust + /// let c: char = '6'; + /// c.is_ascii_digit(); + /// c.is_ascii_hexdigit(); + /// ``` + #[clippy::version = "1.61.0"] + pub IS_DIGIT_ASCII_RADIX, + style, + "use of `char::is_digit(..)` with literal radix of 10 or 16" +} + +declare_clippy_lint! { + /// + /// ### Why is this bad? + /// + /// ### Example + /// ```rust + /// let x = Some(3); + /// x.as_ref().take(); + /// ``` + /// Use instead: + /// ```rust + /// let x = Some(3); + /// x.as_ref(); + /// ``` + #[clippy::version = "1.61.0"] + pub NEEDLESS_OPTION_TAKE, + complexity, + "using `.as_ref().take()` on a temporary value" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Option, @@ -2219,6 +2285,8 @@ impl_lint_pass!(Methods => [ UNNECESSARY_JOIN, ERR_EXPECT, NEEDLESS_OPTION_AS_DEREF, + IS_DIGIT_ASCII_RADIX, + NEEDLESS_OPTION_TAKE, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -2254,7 +2322,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { single_char_add_str::check(cx, expr, args); into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, args); single_char_pattern::check(cx, expr, method_call.ident.name, args); - unnecessary_to_owned::check(cx, expr, method_call.ident.name, args); + unnecessary_to_owned::check(cx, expr, method_call.ident.name, args, self.msrv.as_ref()); }, hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => { let mut info = BinaryExprInfo { @@ -2516,6 +2584,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio }, ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"), ("is_file", []) => filetype_is_file::check(cx, expr, recv), + ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, msrv), ("is_none", []) => check_is_some_is_none(cx, expr, recv, false), ("is_some", []) => check_is_some_is_none(cx, expr, recv, true), ("join", [join_arg]) => { @@ -2574,12 +2643,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio ("splitn" | "rsplitn", [count_arg, pat_arg]) => { if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) { suspicious_splitn::check(cx, name, expr, recv, count); - if count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE) { - str_splitn::check_manual_split_once(cx, name, expr, recv, pat_arg); - } - if count >= 2 { - str_splitn::check_needless_splitn(cx, name, expr, recv, pat_arg, count); - } + str_splitn::check(cx, name, expr, recv, pat_arg, count, msrv); } }, ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => { @@ -2595,6 +2659,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio } } }, + ("take", []) => needless_option_take::check(cx, expr, recv), ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => { implicit_clone::check(cx, name, expr, recv); }, diff --git a/clippy_lints/src/methods/needless_option_take.rs b/clippy_lints/src/methods/needless_option_take.rs new file mode 100644 index 00000000000..829c118d291 --- /dev/null +++ b/clippy_lints/src/methods/needless_option_take.rs @@ -0,0 +1,41 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::match_def_path; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::NEEDLESS_OPTION_TAKE; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) { + // Checks if expression type is equal to sym::Option and if the expr is not a syntactic place + if !recv.is_syntactic_place_expr() && is_expr_option(cx, recv) && has_expr_as_ref_path(cx, recv) { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + NEEDLESS_OPTION_TAKE, + expr.span, + "called `Option::take()` on a temporary value", + "try", + format!( + "{}", + snippet_with_applicability(cx, recv.span, "..", &mut applicability) + ), + applicability, + ); + } +} + +fn is_expr_option(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let expr_type = cx.typeck_results().expr_ty(expr); + is_type_diagnostic_item(cx, expr_type, sym::Option) +} + +fn has_expr_as_ref_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if let Some(ref_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { + return match_def_path(cx, ref_id, &["core", "option", "Option", "as_ref"]); + } + false +} diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 8125930b304..52891eeed06 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -1,36 +1,83 @@ use clippy_utils::consts::{constant, Constant}; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_context; -use clippy_utils::{is_diag_item_method, match_def_path, paths}; +use clippy_utils::usage::local_used_after_expr; +use clippy_utils::visitors::expr_visitor; +use clippy_utils::{is_diag_item_method, match_def_path, meets_msrv, msrvs, path_to_local_id, paths}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, HirId, LangItem, Node, QPath}; +use rustc_hir::intravisit::Visitor; +use rustc_hir::{ + BindingAnnotation, Expr, ExprKind, HirId, LangItem, Local, MatchSource, Node, Pat, PatKind, QPath, Stmt, StmtKind, +}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, adjustment::Adjust}; -use rustc_span::{symbol::sym, Span, SyntaxContext}; +use rustc_middle::ty; +use rustc_semver::RustcVersion; +use rustc_span::{sym, Span, Symbol, SyntaxContext}; -use super::MANUAL_SPLIT_ONCE; +use super::{MANUAL_SPLIT_ONCE, NEEDLESS_SPLITN}; -pub(super) fn check_manual_split_once( +pub(super) fn check( cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>, + count: u128, + msrv: Option<&RustcVersion>, ) { - if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() { + if count < 2 || !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() { return; } - let ctxt = expr.span.ctxt(); - let (method_name, msg, reverse) = if method_name == "splitn" { - ("split_once", "manual implementation of `split_once`", false) - } else { - ("rsplit_once", "manual implementation of `rsplit_once`", true) + let needless = |usage_kind| match usage_kind { + IterUsageKind::Nth(n) => count > n + 1, + IterUsageKind::NextTuple => count > 2, }; - let usage = match parse_iter_usage(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id), reverse) { - Some(x) => x, - None => return, + let manual = count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE); + + match parse_iter_usage(cx, expr.span.ctxt(), cx.tcx.hir().parent_iter(expr.hir_id)) { + Some(usage) if needless(usage.kind) => lint_needless(cx, method_name, expr, self_arg, pat_arg), + Some(usage) if manual => check_manual_split_once(cx, method_name, expr, self_arg, pat_arg, &usage), + None if manual => { + check_manual_split_once_indirect(cx, method_name, expr, self_arg, pat_arg); + }, + _ => {}, + } +} + +fn lint_needless(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>) { + let mut app = Applicability::MachineApplicable; + let r = if method_name == "splitn" { "" } else { "r" }; + + span_lint_and_sugg( + cx, + NEEDLESS_SPLITN, + expr.span, + &format!("unnecessary use of `{r}splitn`"), + "try this", + format!( + "{}.{r}split({})", + snippet_with_context(cx, self_arg.span, expr.span.ctxt(), "..", &mut app).0, + snippet_with_context(cx, pat_arg.span, expr.span.ctxt(), "..", &mut app).0, + ), + app, + ); +} + +fn check_manual_split_once( + cx: &LateContext<'_>, + method_name: &str, + expr: &Expr<'_>, + self_arg: &Expr<'_>, + pat_arg: &Expr<'_>, + usage: &IterUsage, +) { + let ctxt = expr.span.ctxt(); + let (msg, reverse) = if method_name == "splitn" { + ("manual implementation of `split_once`", false) + } else { + ("manual implementation of `rsplit_once`", true) }; let mut app = Applicability::MachineApplicable; @@ -39,84 +86,198 @@ pub(super) fn check_manual_split_once( let sugg = match usage.kind { IterUsageKind::NextTuple => { - format!("{}.{}({})", self_snip, method_name, pat_snip) - }, - IterUsageKind::RNextTuple => format!("{}.{}({}).map(|(x, y)| (y, x))", self_snip, method_name, pat_snip), - IterUsageKind::Next | IterUsageKind::Second => { - let self_deref = { - let adjust = cx.typeck_results().expr_adjustments(self_arg); - if adjust.len() < 2 { - String::new() - } else if cx.typeck_results().expr_ty(self_arg).is_box() - || adjust - .iter() - .any(|a| matches!(a.kind, Adjust::Deref(Some(_))) || a.target.is_box()) - { - format!("&{}", "*".repeat(adjust.len().saturating_sub(1))) - } else { - "*".repeat(adjust.len().saturating_sub(2)) - } - }; - if matches!(usage.kind, IterUsageKind::Next) { - match usage.unwrap_kind { - Some(UnwrapKind::Unwrap) => { - if reverse { - format!("{}.{}({}).unwrap().0", self_snip, method_name, pat_snip) - } else { - format!( - "{}.{}({}).map_or({}{}, |x| x.0)", - self_snip, method_name, pat_snip, self_deref, &self_snip - ) - } - }, - Some(UnwrapKind::QuestionMark) => { - format!( - "{}.{}({}).map_or({}{}, |x| x.0)", - self_snip, method_name, pat_snip, self_deref, &self_snip - ) - }, - None => { - format!( - "Some({}.{}({}).map_or({}{}, |x| x.0))", - &self_snip, method_name, pat_snip, self_deref, &self_snip - ) - }, - } + if reverse { + format!("{self_snip}.rsplit_once({pat_snip}).map(|(x, y)| (y, x))") } else { - match usage.unwrap_kind { - Some(UnwrapKind::Unwrap) => { - if reverse { - // In this case, no better suggestion is offered. - return; - } - format!("{}.{}({}).unwrap().1", self_snip, method_name, pat_snip) - }, - Some(UnwrapKind::QuestionMark) => { - format!("{}.{}({})?.1", self_snip, method_name, pat_snip) - }, - None => { - format!("{}.{}({}).map(|x| x.1)", self_snip, method_name, pat_snip) - }, - } + format!("{self_snip}.split_once({pat_snip})") } }, + IterUsageKind::Nth(1) => { + let (r, field) = if reverse { ("r", 0) } else { ("", 1) }; + + match usage.unwrap_kind { + Some(UnwrapKind::Unwrap) => { + format!("{self_snip}.{r}split_once({pat_snip}).unwrap().{field}") + }, + Some(UnwrapKind::QuestionMark) => { + format!("{self_snip}.{r}split_once({pat_snip})?.{field}") + }, + None => { + format!("{self_snip}.{r}split_once({pat_snip}).map(|x| x.{field})") + }, + } + }, + IterUsageKind::Nth(_) => return, }; span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app); } -enum IterUsageKind { - Next, - Second, - NextTuple, - RNextTuple, +/// checks for +/// +/// ``` +/// let mut iter = "a.b.c".splitn(2, '.'); +/// let a = iter.next(); +/// let b = iter.next(); +/// ``` +fn check_manual_split_once_indirect( + cx: &LateContext<'_>, + method_name: &str, + expr: &Expr<'_>, + self_arg: &Expr<'_>, + pat_arg: &Expr<'_>, +) -> Option<()> { + let ctxt = expr.span.ctxt(); + let mut parents = cx.tcx.hir().parent_iter(expr.hir_id); + if let (_, Node::Local(local)) = parents.next()? + && let PatKind::Binding(BindingAnnotation::Mutable, iter_binding_id, iter_ident, None) = local.pat.kind + && let (iter_stmt_id, Node::Stmt(_)) = parents.next()? + && let (_, Node::Block(enclosing_block)) = parents.next()? + + && let mut stmts = enclosing_block + .stmts + .iter() + .skip_while(|stmt| stmt.hir_id != iter_stmt_id) + .skip(1) + + && let first = indirect_usage(cx, stmts.next()?, iter_binding_id, ctxt)? + && let second = indirect_usage(cx, stmts.next()?, iter_binding_id, ctxt)? + && first.unwrap_kind == second.unwrap_kind + && first.name != second.name + && !local_used_after_expr(cx, iter_binding_id, second.init_expr) + { + let (r, lhs, rhs) = if method_name == "splitn" { + ("", first.name, second.name) + } else { + ("r", second.name, first.name) + }; + let msg = format!("manual implementation of `{r}split_once`"); + + let mut app = Applicability::MachineApplicable; + let self_snip = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0; + let pat_snip = snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0; + + span_lint_and_then(cx, MANUAL_SPLIT_ONCE, local.span, &msg, |diag| { + diag.span_label(first.span, "first usage here"); + diag.span_label(second.span, "second usage here"); + + let unwrap = match first.unwrap_kind { + UnwrapKind::Unwrap => ".unwrap()", + UnwrapKind::QuestionMark => "?", + }; + diag.span_suggestion_verbose( + local.span, + &format!("try `{r}split_once`"), + format!("let ({lhs}, {rhs}) = {self_snip}.{r}split_once({pat_snip}){unwrap};"), + app, + ); + + let remove_msg = format!("remove the `{iter_ident}` usages"); + diag.span_suggestion( + first.span, + &remove_msg, + String::new(), + app, + ); + diag.span_suggestion( + second.span, + &remove_msg, + String::new(), + app, + ); + }); + } + + Some(()) } +#[derive(Debug)] +struct IndirectUsage<'a> { + name: Symbol, + span: Span, + init_expr: &'a Expr<'a>, + unwrap_kind: UnwrapKind, +} + +/// returns `Some(IndirectUsage)` for e.g. +/// +/// ```ignore +/// let name = binding.next()?; +/// let name = binding.next().unwrap(); +/// ``` +fn indirect_usage<'tcx>( + cx: &LateContext<'tcx>, + stmt: &Stmt<'tcx>, + binding: HirId, + ctxt: SyntaxContext, +) -> Option> { + if let StmtKind::Local(Local { + pat: + Pat { + kind: PatKind::Binding(BindingAnnotation::Unannotated, _, ident, None), + .. + }, + init: Some(init_expr), + hir_id: local_hir_id, + .. + }) = stmt.kind + { + let mut path_to_binding = None; + expr_visitor(cx, |expr| { + if path_to_local_id(expr, binding) { + path_to_binding = Some(expr); + } + + path_to_binding.is_none() + }) + .visit_expr(init_expr); + + let mut parents = cx.tcx.hir().parent_iter(path_to_binding?.hir_id); + let iter_usage = parse_iter_usage(cx, ctxt, &mut parents)?; + + let (parent_id, _) = parents.find(|(_, node)| { + !matches!( + node, + Node::Expr(Expr { + kind: ExprKind::Match(.., MatchSource::TryDesugar), + .. + }) + ) + })?; + + if let IterUsage { + kind: IterUsageKind::Nth(0), + unwrap_kind: Some(unwrap_kind), + .. + } = iter_usage + { + if parent_id == *local_hir_id { + return Some(IndirectUsage { + name: ident.name, + span: stmt.span, + init_expr, + unwrap_kind, + }); + } + } + } + + None +} + +#[derive(Debug, Clone, Copy)] +enum IterUsageKind { + Nth(u128), + NextTuple, +} + +#[derive(Debug, PartialEq)] enum UnwrapKind { Unwrap, QuestionMark, } +#[derive(Debug)] struct IterUsage { kind: IterUsageKind, unwrap_kind: Option, @@ -128,7 +289,6 @@ fn parse_iter_usage<'tcx>( cx: &LateContext<'tcx>, ctxt: SyntaxContext, mut iter: impl Iterator)>, - reverse: bool, ) -> Option { let (kind, span) = match iter.next() { Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => { @@ -141,13 +301,7 @@ fn parse_iter_usage<'tcx>( let iter_id = cx.tcx.get_diagnostic_item(sym::Iterator)?; match (name.ident.as_str(), args) { - ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => { - if reverse { - (IterUsageKind::Second, e.span) - } else { - (IterUsageKind::Next, e.span) - } - }, + ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Nth(0), e.span), ("next_tuple", []) => { return if_chain! { if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE); @@ -157,7 +311,7 @@ fn parse_iter_usage<'tcx>( if subs.len() == 2; then { Some(IterUsage { - kind: if reverse { IterUsageKind::RNextTuple } else { IterUsageKind::NextTuple }, + kind: IterUsageKind::NextTuple, span: e.span, unwrap_kind: None }) @@ -185,11 +339,7 @@ fn parse_iter_usage<'tcx>( } } }; - match if reverse { idx ^ 1 } else { idx } { - 0 => (IterUsageKind::Next, span), - 1 => (IterUsageKind::Second, span), - _ => return None, - } + (IterUsageKind::Nth(idx), span) } else { return None; } @@ -238,86 +388,3 @@ fn parse_iter_usage<'tcx>( span, }) } - -use super::NEEDLESS_SPLITN; - -pub(super) fn check_needless_splitn( - cx: &LateContext<'_>, - method_name: &str, - expr: &Expr<'_>, - self_arg: &Expr<'_>, - pat_arg: &Expr<'_>, - count: u128, -) { - if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() { - return; - } - let ctxt = expr.span.ctxt(); - let mut app = Applicability::MachineApplicable; - let (reverse, message) = if method_name == "splitn" { - (false, "unnecessary use of `splitn`") - } else { - (true, "unnecessary use of `rsplitn`") - }; - if_chain! { - if count >= 2; - if check_iter(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id), count); - then { - span_lint_and_sugg( - cx, - NEEDLESS_SPLITN, - expr.span, - message, - "try this", - format!( - "{}.{}({})", - snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0, - if reverse {"rsplit"} else {"split"}, - snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0 - ), - app, - ); - } - } -} - -fn check_iter<'tcx>( - cx: &LateContext<'tcx>, - ctxt: SyntaxContext, - mut iter: impl Iterator)>, - count: u128, -) -> bool { - match iter.next() { - Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => { - let (name, args) = if let ExprKind::MethodCall(name, [_, args @ ..], _) = e.kind { - (name, args) - } else { - return false; - }; - if_chain! { - if let Some(did) = cx.typeck_results().type_dependent_def_id(e.hir_id); - if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator); - then { - match (name.ident.as_str(), args) { - ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => { - return true; - }, - ("next_tuple", []) if count > 2 => { - return true; - }, - ("nth", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => { - if let Some((Constant::Int(idx), _)) = constant(cx, cx.typeck_results(), idx_expr) { - if count > idx + 1 { - return true; - } - } - }, - _ => return false, - } - } - } - }, - _ => return false, - }; - false -} diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 1555758fc4a..02b882e8b55 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -5,6 +5,8 @@ use clippy_utils::source::snippet_opt; use clippy_utils::ty::{ contains_ty, get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs, }; +use clippy_utils::{meets_msrv, msrvs}; + use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item}; use rustc_errors::Applicability; use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind}; @@ -13,12 +15,19 @@ use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; use rustc_middle::ty::{self, PredicateKind, ProjectionPredicate, TraitPredicate, Ty}; +use rustc_semver::RustcVersion; use rustc_span::{sym, Symbol}; use std::cmp::max; use super::UNNECESSARY_TO_OWNED; -pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, args: &'tcx [Expr<'tcx>]) { +pub fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + method_name: Symbol, + args: &'tcx [Expr<'tcx>], + msrv: Option<&RustcVersion>, +) { if_chain! { if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let [receiver] = args; @@ -33,7 +42,7 @@ pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: if check_addr_of_expr(cx, expr, method_name, method_def_id, receiver) { return; } - if check_into_iter_call_arg(cx, expr, method_name, receiver) { + if check_into_iter_call_arg(cx, expr, method_name, receiver, msrv) { return; } check_other_call_arg(cx, expr, method_name, receiver); @@ -178,7 +187,13 @@ fn check_addr_of_expr( /// Checks whether `expr` is an argument in an `into_iter` call and, if so, determines whether its /// call of a `to_owned`-like function is unnecessary. -fn check_into_iter_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool { +fn check_into_iter_call_arg( + cx: &LateContext<'_>, + expr: &Expr<'_>, + method_name: Symbol, + receiver: &Expr<'_>, + msrv: Option<&RustcVersion>, +) -> bool { if_chain! { if let Some(parent) = get_parent_expr(cx, expr); if let Some(callee_def_id) = fn_def_id(cx, parent); @@ -192,7 +207,7 @@ fn check_into_iter_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) { return true; } - let cloned_or_copied = if is_copy(cx, item_ty) { "copied" } else { "cloned" }; + let cloned_or_copied = if is_copy(cx, item_ty) && meets_msrv(msrv, &msrvs::ITERATOR_COPIED) { "copied" } else { "cloned" }; // The next suggestion may be incorrect because the removal of the `to_owned`-like // function could cause the iterator to hold a reference to a resource that is used // mutably. See https://github.com/rust-lang/rust-clippy/issues/8148. diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index aecfea9c141..4b368d3ffae 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -14,7 +14,7 @@ const CONVENTIONS: [(&[Convention], &[SelfKind]); 9] = [ (&[Convention::StartsWith("as_")], &[SelfKind::Ref, SelfKind::RefMut]), (&[Convention::StartsWith("from_")], &[SelfKind::No]), (&[Convention::StartsWith("into_")], &[SelfKind::Value]), - (&[Convention::StartsWith("is_")], &[SelfKind::Ref, SelfKind::No]), + (&[Convention::StartsWith("is_")], &[SelfKind::RefMut, SelfKind::Ref, SelfKind::No]), (&[Convention::Eq("to_mut")], &[SelfKind::RefMut]), (&[Convention::StartsWith("to_"), Convention::EndsWith("_mut")], &[SelfKind::RefMut]), diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index d955fad7d41..6860b60acbd 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -361,7 +361,7 @@ impl MiscEarlyLints { // See for a regression. // FIXME: Find a better way to detect those cases. let lit_snip = match snippet_opt(cx, lit.span) { - Some(snip) if snip.chars().next().map_or(false, |c| c.is_digit(10)) => snip, + Some(snip) if snip.chars().next().map_or(false, |c| c.is_ascii_digit()) => snip, _ => return, }; diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index ac2f16b49e3..0d953299189 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -44,7 +44,7 @@ declare_clippy_lint! { /// pub struct PubBaz; /// impl PubBaz { /// fn private() {} // ok - /// pub fn not_ptrivate() {} // missing #[inline] + /// pub fn not_private() {} // missing #[inline] /// } /// /// impl Bar for PubBaz { @@ -97,7 +97,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { let attrs = cx.tcx.hir().attrs(it.hir_id()); check_missing_inline_attrs(cx, attrs, it.span, desc); }, - hir::ItemKind::Trait(ref _is_auto, ref _unsafe, ref _generics, _bounds, trait_items) => { + hir::ItemKind::Trait(ref _is_auto, ref _unsafe, _generics, _bounds, trait_items) => { // note: we need to check if the trait is exported so we can't use // `LateLintPass::check_trait_item` here. for tit in trait_items { diff --git a/clippy_lints/src/needless_bitwise_bool.rs b/clippy_lints/src/needless_bitwise_bool.rs index a8a8d174a82..95395e2e136 100644 --- a/clippy_lints/src/needless_bitwise_bool.rs +++ b/clippy_lints/src/needless_bitwise_bool.rs @@ -53,7 +53,7 @@ fn is_bitwise_operation(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { false } -fn suggession_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { +fn suggesstion_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { if let ExprKind::Binary(ref op, left, right) = expr.kind { if let (Some(l_snippet), Some(r_snippet)) = (snippet_opt(cx, left.span), snippet_opt(cx, right.span)) { let op_snippet = match op.node { @@ -75,7 +75,7 @@ impl LateLintPass<'_> for NeedlessBitwiseBool { expr.span, "use of bitwise operator instead of lazy operator between booleans", |diag| { - if let Some(sugg) = suggession_snippet(cx, expr) { + if let Some(sugg) = suggesstion_snippet(cx, expr) { diag.span_suggestion(expr.span, "try", sugg, Applicability::MachineApplicable); } }, diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index 9957afcbf04..b70871b38be 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -1,10 +1,14 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::path_to_local; use clippy_utils::source::snippet_opt; -use clippy_utils::visitors::{expr_visitor, is_local_used}; -use rustc_errors::Applicability; +use clippy_utils::ty::needs_ordered_drop; +use clippy_utils::visitors::{expr_visitor, expr_visitor_no_bodies, is_local_used}; +use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::intravisit::Visitor; -use rustc_hir::{Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, StmtKind}; +use rustc_hir::{ + BindingAnnotation, Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, + StmtKind, +}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; @@ -73,6 +77,31 @@ fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> seen } +fn contains_let(cond: &Expr<'_>) -> bool { + let mut seen = false; + expr_visitor_no_bodies(|expr| { + if let ExprKind::Let(_) = expr.kind { + seen = true; + } + + !seen + }) + .visit_expr(cond); + + seen +} + +fn stmt_needs_ordered_drop(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { + let StmtKind::Local(local) = stmt.kind else { return false }; + !local.pat.walk_short(|pat| { + if let PatKind::Binding(.., None) = pat.kind { + !needs_ordered_drop(cx, cx.typeck_results().pat_ty(pat)) + } else { + true + } + }) +} + #[derive(Debug)] struct LocalAssign { lhs_id: HirId, @@ -187,11 +216,14 @@ fn first_usage<'tcx>( local_stmt_id: HirId, block: &'tcx Block<'tcx>, ) -> Option> { + let significant_drop = needs_ordered_drop(cx, cx.typeck_results().node_type(binding_id)); + block .stmts .iter() .skip_while(|stmt| stmt.hir_id != local_stmt_id) .skip(1) + .take_while(|stmt| !significant_drop || !stmt_needs_ordered_drop(cx, stmt)) .find(|&stmt| is_local_used(cx, stmt, binding_id)) .and_then(|stmt| match stmt.kind { StmtKind::Expr(expr) => Some(Usage { @@ -235,12 +267,15 @@ fn check<'tcx>( match usage.expr.kind { ExprKind::Assign(..) => { let assign = LocalAssign::new(cx, usage.expr, binding_id)?; + let mut msg_span = MultiSpan::from_spans(vec![local_stmt.span, assign.span]); + msg_span.push_span_label(local_stmt.span, "created here"); + msg_span.push_span_label(assign.span, "initialised here"); span_lint_and_then( cx, NEEDLESS_LATE_INIT, - local_stmt.span, - "unneeded late initalization", + msg_span, + "unneeded late initialization", |diag| { diag.tool_only_span_suggestion( local_stmt.span, @@ -258,14 +293,14 @@ fn check<'tcx>( }, ); }, - ExprKind::If(_, then_expr, Some(else_expr)) => { + ExprKind::If(cond, then_expr, Some(else_expr)) if !contains_let(cond) => { let (applicability, suggestions) = assignment_suggestions(cx, binding_id, [then_expr, else_expr])?; span_lint_and_then( cx, NEEDLESS_LATE_INIT, local_stmt.span, - "unneeded late initalization", + "unneeded late initialization", |diag| { diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability); @@ -296,7 +331,7 @@ fn check<'tcx>( cx, NEEDLESS_LATE_INIT, local_stmt.span, - "unneeded late initalization", + "unneeded late initialization", |diag| { diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability); @@ -333,12 +368,11 @@ fn check<'tcx>( impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { let mut parents = cx.tcx.hir().parent_iter(local.hir_id); - if_chain! { if let Local { init: None, pat: &Pat { - kind: PatKind::Binding(_, binding_id, _, None), + kind: PatKind::Binding(BindingAnnotation::Unannotated, binding_id, _, None), .. }, source: LocalSource::Normal, diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index 96c00c205ff..2f733f221d5 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { if let hir::ItemKind::Impl(hir::Impl { of_trait: None, - ref generics, + generics, self_ty: impl_self_ty, items, .. diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 0d0c88b02c7..e3bc40c4b49 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -197,7 +197,7 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> { if interned_name.chars().any(char::is_uppercase) { return; } - if interned_name.chars().all(|c| c.is_digit(10) || c == '_') { + if interned_name.chars().all(|c| c.is_ascii_digit() || c == '_') { span_lint( self.0.cx, JUST_UNDERSCORES_AND_DIGITS, diff --git a/clippy_lints/src/octal_escapes.rs b/clippy_lints/src/octal_escapes.rs index c19cea66104..e8532db4f71 100644 --- a/clippy_lints/src/octal_escapes.rs +++ b/clippy_lints/src/octal_escapes.rs @@ -25,7 +25,7 @@ declare_clippy_lint! { /// /// ### Known problems /// The actual meaning can be the intended one. `\x00` can be used in these - /// cases to be unambigious. + /// cases to be unambiguous. /// /// The lint does not trigger for format strings in `print!()`, `write!()` /// and friends since the string is already preprocessed when Clippy lints diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index 8e61f234776..beb812793f8 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -1,6 +1,7 @@ use std::collections::VecDeque; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_lint_allowed; use itertools::{izip, Itertools}; use rustc_ast::{walk_list, Label, Mutability}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -8,7 +9,7 @@ use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; -use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; +use rustc_hir::intravisit::{walk_expr, walk_stmt, FnKind, Visitor}; use rustc_hir::{ Arm, Block, Body, Expr, ExprKind, Guard, HirId, ImplicitSelfKind, Let, Local, Pat, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TyKind, UnOp, @@ -33,6 +34,9 @@ declare_clippy_lint! { /// and the assigned variables are also only in recursion, it is useless. /// /// ### Known problems + /// Too many code paths in the linting code are currently untested and prone to produce false + /// positives or are prone to have performance implications. + /// /// In some cases, this would not catch all useless arguments. /// /// ```rust @@ -85,7 +89,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.60.0"] pub ONLY_USED_IN_RECURSION, - complexity, + nursery, "arguments that is only used in recursion can be removed" } declare_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]); @@ -100,6 +104,9 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { _: Span, id: HirId, ) { + if is_lint_allowed(cx, ONLY_USED_IN_RECURSION, id) { + return; + } if let FnKind::ItemFn(ident, ..) | FnKind::Method(ident, ..) = kind { let def_id = id.owner.to_def_id(); let data = cx.tcx.def_path(def_id).data; @@ -145,7 +152,8 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { is_method: matches!(kind, FnKind::Method(..)), has_self, ty_res, - ty_ctx: cx.tcx, + tcx: cx.tcx, + visited_exprs: FxHashSet::default(), }; visitor.visit_expr(&body.value); @@ -206,19 +214,13 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { } pub fn is_primitive(ty: Ty<'_>) -> bool { - match ty.kind() { - ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true, - ty::Ref(_, t, _) => is_primitive(*t), - _ => false, - } + let ty = ty.peel_refs(); + ty.is_primitive() || ty.is_str() } pub fn is_array(ty: Ty<'_>) -> bool { - match ty.kind() { - ty::Array(..) | ty::Slice(..) => true, - ty::Ref(_, t, _) => is_array(*t), - _ => false, - } + let ty = ty.peel_refs(); + ty.is_array() || ty.is_array_slice() } /// This builds the graph of side effect. @@ -250,40 +252,30 @@ pub struct SideEffectVisit<'tcx> { is_method: bool, has_self: bool, ty_res: &'tcx TypeckResults<'tcx>, - ty_ctx: TyCtxt<'tcx>, + tcx: TyCtxt<'tcx>, + visited_exprs: FxHashSet, } impl<'tcx> Visitor<'tcx> for SideEffectVisit<'tcx> { - fn visit_block(&mut self, b: &'tcx Block<'tcx>) { - b.stmts.iter().for_each(|stmt| { - self.visit_stmt(stmt); - self.ret_vars.clear(); - }); - walk_list!(self, visit_expr, b.expr); - } - fn visit_stmt(&mut self, s: &'tcx Stmt<'tcx>) { match s.kind { StmtKind::Local(Local { pat, init: Some(init), .. }) => { self.visit_pat_expr(pat, init, false); - self.ret_vars.clear(); }, - StmtKind::Item(i) => { - let item = self.ty_ctx.hir().item(i); - self.visit_item(item); - self.ret_vars.clear(); - }, - StmtKind::Expr(e) | StmtKind::Semi(e) => { - self.visit_expr(e); - self.ret_vars.clear(); + StmtKind::Item(_) | StmtKind::Expr(_) | StmtKind::Semi(_) => { + walk_stmt(self, s); }, StmtKind::Local(_) => {}, } + self.ret_vars.clear(); } fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { + if !self.visited_exprs.insert(ex.hir_id) { + return; + } match ex.kind { ExprKind::Array(exprs) | ExprKind::Tup(exprs) => { self.ret_vars = exprs @@ -307,7 +299,7 @@ impl<'tcx> Visitor<'tcx> for SideEffectVisit<'tcx> { ExprKind::Match(expr, arms, _) => self.visit_match(expr, arms), // since analysing the closure is not easy, just set all variables in it to side-effect ExprKind::Closure(_, _, body_id, _, _) => { - let body = self.ty_ctx.hir().body(body_id); + let body = self.tcx.hir().body(body_id); self.visit_body(body); let vars = std::mem::take(&mut self.ret_vars); self.add_side_effect(vars); diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index c9f807f2aa3..ea5a8f0858b 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -78,7 +78,7 @@ fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool { /// A struct containing information about occurrences of the /// `if let Some(..) = .. else` construct that this lint detects. -struct OptionIfLetElseOccurence { +struct OptionIfLetElseOccurrence { option: String, method_sugg: String, some_expr: String, @@ -100,9 +100,9 @@ fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: boo } /// If this expression is the option if let/else construct we're detecting, then -/// this function returns an `OptionIfLetElseOccurence` struct with details if +/// this function returns an `OptionIfLetElseOccurrence` struct with details if /// this construct is found, or None if this construct is not found. -fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option { +fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option { if_chain! { if !expr.span.from_expansion(); // Don't lint macros, because it behaves weirdly if !in_constant(cx, expr.hir_id); @@ -154,7 +154,7 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> } } } - Some(OptionIfLetElseOccurence { + Some(OptionIfLetElseOccurrence { option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut), method_sugg: method_sugg.to_string(), some_expr: format!("|{}{}| {}", capture_mut, capture_name, Sugg::hir_with_macro_callsite(cx, some_body, "..")), diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 48a2666a2e0..c35eeeac67a 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -3,6 +3,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::expr_sig; +use clippy_utils::visitors::contains_unsafe_block; use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, paths}; use if_chain::if_chain; use rustc_errors::{Applicability, MultiSpan}; @@ -10,9 +11,9 @@ use rustc_hir::def_id::DefId; use rustc_hir::hir_id::HirIdMap; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{ - self as hir, AnonConst, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, FnRetTy, GenericArg, + self as hir, AnonConst, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg, ImplItemKind, ItemKind, Lifetime, LifetimeName, Mutability, Node, Param, ParamName, PatKind, QPath, TraitFn, - TraitItem, TraitItemKind, TyKind, + TraitItem, TraitItemKind, TyKind, Unsafety, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; @@ -88,19 +89,26 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// This lint checks for functions that take immutable - /// references and return mutable ones. + /// This lint checks for functions that take immutable references and return + /// mutable ones. This will not trigger if no unsafe code exists as there + /// are multiple safe functions which will do this transformation + /// + /// To be on the conservative side, if there's at least one mutable + /// reference with the output lifetime, this lint will not trigger. /// /// ### Why is this bad? - /// This is trivially unsound, as one can create two - /// mutable references from the same (immutable!) source. - /// This [error](https://github.com/rust-lang/rust/issues/39465) - /// actually lead to an interim Rust release 1.15.1. + /// Creating a mutable reference which can be repeatably derived from an + /// immutable reference is unsound as it allows creating multiple live + /// mutable references to the same object. + /// + /// This [error](https://github.com/rust-lang/rust/issues/39465) actually + /// lead to an interim Rust release 1.15.1. /// /// ### Known problems - /// To be on the conservative side, if there's at least one - /// mutable reference with the output lifetime, this lint will not trigger. - /// In practice, this case is unlikely anyway. + /// This pattern is used by memory allocators to allow allocating multiple + /// objects while returning mutable references to each one. So long as + /// different mutable references are returned each time such a function may + /// be safe. /// /// ### Example /// ```ignore @@ -145,7 +153,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { return; } - check_mut_from_ref(cx, sig.decl); + check_mut_from_ref(cx, sig, None); for arg in check_fn_args( cx, cx.tcx.fn_sig(item.def_id).skip_binder().inputs(), @@ -170,10 +178,10 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { let hir = cx.tcx.hir(); let mut parents = hir.parent_iter(body.value.hir_id); - let (item_id, decl, is_trait_item) = match parents.next() { + let (item_id, sig, is_trait_item) = match parents.next() { Some((_, Node::Item(i))) => { if let ItemKind::Fn(sig, ..) = &i.kind { - (i.def_id, sig.decl, false) + (i.def_id, sig, false) } else { return; } @@ -185,14 +193,14 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { return; } if let ImplItemKind::Fn(sig, _) = &i.kind { - (i.def_id, sig.decl, false) + (i.def_id, sig, false) } else { return; } }, Some((_, Node::TraitItem(i))) => { if let TraitItemKind::Fn(sig, _) = &i.kind { - (i.def_id, sig.decl, true) + (i.def_id, sig, true) } else { return; } @@ -200,7 +208,8 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { _ => return, }; - check_mut_from_ref(cx, decl); + check_mut_from_ref(cx, sig, Some(body)); + let decl = sig.decl; let sig = cx.tcx.fn_sig(item_id).skip_binder(); let lint_args: Vec<_> = check_fn_args(cx, sig.inputs(), decl.inputs, body.params) .filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not) @@ -473,31 +482,31 @@ fn check_fn_args<'cx, 'tcx: 'cx>( }) } -fn check_mut_from_ref(cx: &LateContext<'_>, decl: &FnDecl<'_>) { - if let FnRetTy::Return(ty) = decl.output { - if let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) { - let mut immutables = vec![]; - for (_, mutbl, argspan) in decl - .inputs - .iter() - .filter_map(get_rptr_lm) - .filter(|&(lt, _, _)| lt.name == out.name) - { - if mutbl == Mutability::Mut { - return; - } - immutables.push(argspan); - } - if immutables.is_empty() { - return; - } +fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Option<&'tcx Body<'_>>) { + if let FnRetTy::Return(ty) = sig.decl.output + && let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) + { + let args: Option> = sig + .decl + .inputs + .iter() + .filter_map(get_rptr_lm) + .filter(|&(lt, _, _)| lt.name == out.name) + .map(|(_, mutability, span)| (mutability == Mutability::Not).then(|| span)) + .collect(); + if let Some(args) = args + && !args.is_empty() + && body.map_or(true, |body| { + sig.header.unsafety == Unsafety::Unsafe || contains_unsafe_block(cx, &body.value) + }) + { span_lint_and_then( cx, MUT_FROM_REF, ty.span, "mutable borrow from immutable input(s)", |diag| { - let ms = MultiSpan::from_spans(immutables); + let ms = MultiSpan::from_spans(args); diag.span_note(ms, "immutable borrow here"); }, ); diff --git a/clippy_lints/src/pub_use.rs b/clippy_lints/src/pub_use.rs new file mode 100644 index 00000000000..9d2b0cedb60 --- /dev/null +++ b/clippy_lints/src/pub_use.rs @@ -0,0 +1,56 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_ast::ast::{Item, ItemKind, VisibilityKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// + /// Restricts the usage of `pub use ...` + /// + /// ### Why is this bad? + /// + /// `pub use` is usually fine, but a project may wish to limit `pub use` instances to prevent + /// unintentional exports or to encourage placing exported items directly in public modules + /// + /// ### Example + /// ```rust + /// pub mod outer { + /// mod inner { + /// pub struct Test {} + /// } + /// pub use inner::Test; + /// } + /// + /// use outer::Test; + /// ``` + /// Use instead: + /// ```rust + /// pub mod outer { + /// pub struct Test {} + /// } + /// + /// use outer::Test; + /// ``` + #[clippy::version = "1.62.0"] + pub PUB_USE, + restriction, + "restricts the usage of `pub use`" +} +declare_lint_pass!(PubUse => [PUB_USE]); + +impl EarlyLintPass for PubUse { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if let ItemKind::Use(_) = item.kind && + let VisibilityKind::Public = item.vis.kind { + span_lint_and_help( + cx, + PUB_USE, + item.span, + "using `pub use`", + None, + "move the exported item to a public module instead", + ); + } + } +} diff --git a/clippy_lints/src/redundant_pub_crate.rs b/clippy_lints/src/redundant_pub_crate.rs index e2e2400f8e2..323326381d4 100644 --- a/clippy_lints/src/redundant_pub_crate.rs +++ b/clippy_lints/src/redundant_pub_crate.rs @@ -1,10 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::def_id::CRATE_DEF_ID; +use rustc_span::hygiene::MacroKind; declare_clippy_lint! { /// ### What it does @@ -43,8 +45,11 @@ impl_lint_pass!(RedundantPubCrate => [REDUNDANT_PUB_CRATE]); impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if cx.tcx.visibility(item.def_id) == ty::Visibility::Restricted(CRATE_DEF_ID.to_def_id()) { - if !cx.access_levels.is_exported(item.def_id) && self.is_exported.last() == Some(&false) { + if_chain! { + if cx.tcx.visibility(item.def_id) == ty::Visibility::Restricted(CRATE_DEF_ID.to_def_id()); + if !cx.access_levels.is_exported(item.def_id) && self.is_exported.last() == Some(&false); + if is_not_macro_export(item); + then { let span = item.span.with_hi(item.ident.span.hi()); let descr = cx.tcx.def_kind(item.def_id).descr(item.def_id.to_def_id()); span_lint_and_then( @@ -75,3 +80,15 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { } } } + +fn is_not_macro_export<'tcx>(item: &'tcx Item<'tcx>) -> bool { + if let ItemKind::Use(path, _) = item.kind { + if let Res::Def(DefKind::Macro(MacroKind::Bang), _) = path.res { + return false; + } + } else if let ItemKind::Macro(..) = item.kind { + return false; + } + + true +} diff --git a/clippy_lints/src/renamed_lints.rs b/clippy_lints/src/renamed_lints.rs new file mode 100644 index 00000000000..bfc03116fe2 --- /dev/null +++ b/clippy_lints/src/renamed_lints.rs @@ -0,0 +1,39 @@ +// This file is managed by `cargo dev rename_lint`. Prefer using that when possible. + +#[rustfmt::skip] +pub static RENAMED_LINTS: &[(&str, &str)] = &[ + ("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions"), + ("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions"), + ("clippy::box_vec", "clippy::box_collection"), + ("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"), + ("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"), + ("clippy::disallowed_method", "clippy::disallowed_methods"), + ("clippy::disallowed_type", "clippy::disallowed_types"), + ("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"), + ("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"), + ("clippy::identity_conversion", "clippy::useless_conversion"), + ("clippy::if_let_some_result", "clippy::match_result_ok"), + ("clippy::new_without_default_derive", "clippy::new_without_default"), + ("clippy::option_and_then_some", "clippy::bind_instead_of_map"), + ("clippy::option_expect_used", "clippy::expect_used"), + ("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"), + ("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"), + ("clippy::option_unwrap_used", "clippy::unwrap_used"), + ("clippy::ref_in_deref", "clippy::needless_borrow"), + ("clippy::result_expect_used", "clippy::expect_used"), + ("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"), + ("clippy::result_unwrap_used", "clippy::unwrap_used"), + ("clippy::single_char_push_str", "clippy::single_char_add_str"), + ("clippy::stutter", "clippy::module_name_repetitions"), + ("clippy::to_string_in_display", "clippy::recursive_format_impl"), + ("clippy::zero_width_space", "clippy::invisible_characters"), + ("clippy::drop_bounds", "drop_bounds"), + ("clippy::into_iter_on_array", "array_into_iter"), + ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), + ("clippy::invalid_ref", "invalid_value"), + ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"), + ("clippy::panic_params", "non_fmt_panics"), + ("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"), + ("clippy::unknown_clippy_lints", "unknown_lints"), + ("clippy::unused_label", "unused_labels"), +]; diff --git a/clippy_lints/src/same_name_method.rs b/clippy_lints/src/same_name_method.rs index a01e2f2db3a..f63925a2f14 100644 --- a/clippy_lints/src/same_name_method.rs +++ b/clippy_lints/src/same_name_method.rs @@ -51,114 +51,110 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { let mut map = FxHashMap::::default(); for id in cx.tcx.hir().items() { - if !matches!(cx.tcx.hir().def_kind(id.def_id), DefKind::Impl) { - continue; - } - - let item = cx.tcx.hir().item(id); - if let ItemKind::Impl(Impl { - items, - of_trait, - self_ty, - .. - }) = &item.kind + if matches!(cx.tcx.hir().def_kind(id.def_id), DefKind::Impl) + && let item = cx.tcx.hir().item(id) + && let ItemKind::Impl(Impl { + items, + of_trait, + self_ty, + .. + }) = &item.kind + && let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind { - if let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind { - if !map.contains_key(res) { - map.insert( - *res, - ExistingName { - impl_methods: BTreeMap::new(), - trait_methods: BTreeMap::new(), - }, - ); - } - let existing_name = map.get_mut(res).unwrap(); - - match of_trait { - Some(trait_ref) => { - let mut methods_in_trait: BTreeSet = if_chain! { - if let Some(Node::TraitRef(TraitRef { path, .. })) = - cx.tcx.hir().find(trait_ref.hir_ref_id); - if let Res::Def(DefKind::Trait, did) = path.res; - then{ - // FIXME: if - // `rustc_middle::ty::assoc::AssocItems::items` is public, - // we can iterate its keys instead of `in_definition_order`, - // which's more efficient - cx.tcx - .associated_items(did) - .in_definition_order() - .filter(|assoc_item| { - matches!(assoc_item.kind, AssocKind::Fn) - }) - .map(|assoc_item| assoc_item.name) - .collect() - }else{ - BTreeSet::new() - } - }; - - let mut check_trait_method = |method_name: Symbol, trait_method_span: Span| { - if let Some(impl_span) = existing_name.impl_methods.get(&method_name) { - span_lint_and_then( - cx, - SAME_NAME_METHOD, - *impl_span, - "method's name is the same as an existing method in a trait", - |diag| { - diag.span_note( - trait_method_span, - &format!("existing `{}` defined here", method_name), - ); - }, - ); - } - if let Some(v) = existing_name.trait_methods.get_mut(&method_name) { - v.push(trait_method_span); - } else { - existing_name.trait_methods.insert(method_name, vec![trait_method_span]); - } - }; - - for impl_item_ref in (*items).iter().filter(|impl_item_ref| { - matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. }) - }) { - let method_name = impl_item_ref.ident.name; - methods_in_trait.remove(&method_name); - check_trait_method(method_name, impl_item_ref.span); - } - - for method_name in methods_in_trait { - check_trait_method(method_name, item.span); - } + if !map.contains_key(res) { + map.insert( + *res, + ExistingName { + impl_methods: BTreeMap::new(), + trait_methods: BTreeMap::new(), }, - None => { - for impl_item_ref in (*items).iter().filter(|impl_item_ref| { - matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. }) - }) { - let method_name = impl_item_ref.ident.name; - let impl_span = impl_item_ref.span; - if let Some(trait_spans) = existing_name.trait_methods.get(&method_name) { - span_lint_and_then( - cx, - SAME_NAME_METHOD, - impl_span, - "method's name is the same as an existing method in a trait", - |diag| { - // TODO should we `span_note` on every trait? - // iterate on trait_spans? - diag.span_note( - trait_spans[0], - &format!("existing `{}` defined here", method_name), - ); - }, - ); - } - existing_name.impl_methods.insert(method_name, impl_span); + ); + } + let existing_name = map.get_mut(res).unwrap(); + + match of_trait { + Some(trait_ref) => { + let mut methods_in_trait: BTreeSet = if_chain! { + if let Some(Node::TraitRef(TraitRef { path, .. })) = + cx.tcx.hir().find(trait_ref.hir_ref_id); + if let Res::Def(DefKind::Trait, did) = path.res; + then{ + // FIXME: if + // `rustc_middle::ty::assoc::AssocItems::items` is public, + // we can iterate its keys instead of `in_definition_order`, + // which's more efficient + cx.tcx + .associated_items(did) + .in_definition_order() + .filter(|assoc_item| { + matches!(assoc_item.kind, AssocKind::Fn) + }) + .map(|assoc_item| assoc_item.name) + .collect() + }else{ + BTreeSet::new() } - }, - } + }; + + let mut check_trait_method = |method_name: Symbol, trait_method_span: Span| { + if let Some(impl_span) = existing_name.impl_methods.get(&method_name) { + span_lint_and_then( + cx, + SAME_NAME_METHOD, + *impl_span, + "method's name is the same as an existing method in a trait", + |diag| { + diag.span_note( + trait_method_span, + &format!("existing `{}` defined here", method_name), + ); + }, + ); + } + if let Some(v) = existing_name.trait_methods.get_mut(&method_name) { + v.push(trait_method_span); + } else { + existing_name.trait_methods.insert(method_name, vec![trait_method_span]); + } + }; + + for impl_item_ref in (*items).iter().filter(|impl_item_ref| { + matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. }) + }) { + let method_name = impl_item_ref.ident.name; + methods_in_trait.remove(&method_name); + check_trait_method(method_name, impl_item_ref.span); + } + + for method_name in methods_in_trait { + check_trait_method(method_name, item.span); + } + }, + None => { + for impl_item_ref in (*items).iter().filter(|impl_item_ref| { + matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. }) + }) { + let method_name = impl_item_ref.ident.name; + let impl_span = impl_item_ref.span; + if let Some(trait_spans) = existing_name.trait_methods.get(&method_name) { + span_lint_and_then( + cx, + SAME_NAME_METHOD, + impl_span, + "method's name is the same as an existing method in a trait", + |diag| { + // TODO should we `span_note` on every trait? + // iterate on trait_spans? + diag.span_note( + trait_spans[0], + &format!("existing `{}` defined here", method_name), + ); + }, + ); + } + existing_name.impl_methods.insert(method_name, impl_span); + } + }, } } } diff --git a/clippy_lints/src/stable_sort_primitive.rs b/clippy_lints/src/stable_sort_primitive.rs index bcd28b42978..a6c685df721 100644 --- a/clippy_lints/src/stable_sort_primitive.rs +++ b/clippy_lints/src/stable_sort_primitive.rs @@ -9,15 +9,25 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// ### What it does /// When sorting primitive values (integers, bools, chars, as well - /// as arrays, slices, and tuples of such items), it is better to + /// as arrays, slices, and tuples of such items), it is typically better to /// use an unstable sort than a stable sort. /// /// ### Why is this bad? - /// Using a stable sort consumes more memory and cpu cycles. Because - /// values which compare equal are identical, preserving their + /// Typically, using a stable sort consumes more memory and cpu cycles. + /// Because values which compare equal are identical, preserving their /// relative order (the guarantee that a stable sort provides) means /// nothing, while the extra costs still apply. /// + /// ### Known problems + /// + /// As pointed out in + /// [issue #8241](https://github.com/rust-lang/rust-clippy/issues/8241), + /// a stable sort can instead be significantly faster for certain scenarios + /// (eg. when a sorted vector is extended with new data and resorted). + /// + /// For more information and benchmarking results, please refer to the + /// issue linked above. + /// /// ### Example /// ```rust /// let mut vec = vec![2, 1, 3]; @@ -30,7 +40,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.47.0"] pub STABLE_SORT_PRIMITIVE, - perf, + pedantic, "use of sort() when sort_unstable() is equivalent" } @@ -126,7 +136,7 @@ impl LateLintPass<'_> for StableSortPrimitive { Applicability::MachineApplicable, ); diag.note( - "an unstable sort would perform faster without any observable difference for this data type", + "an unstable sort typically performs faster without any observable difference for this data type", ); }, ); diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 3573f632a36..7c196ccaa8c 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -5,6 +5,7 @@ use clippy_utils::{get_parent_expr, is_lint_allowed, match_function_call, method use clippy_utils::{peel_blocks, SpanlessEq}; use if_chain::if_chain; use rustc_errors::Applicability; +use rustc_hir::def_id::DefId; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -451,3 +452,58 @@ impl<'tcx> LateLintPass<'tcx> for StringToString { } } } + +declare_clippy_lint! { + /// ### What it does + /// Warns about calling `str::trim` (or variants) before `str::split_whitespace`. + /// + /// ### Why is this bad? + /// `split_whitespace` already ignores leading and trailing whitespace. + /// + /// ### Example + /// ```rust + /// " A B C ".trim().split_whitespace(); + /// ``` + /// Use instead: + /// ```rust + /// " A B C ".split_whitespace(); + /// ``` + #[clippy::version = "1.62.0"] + pub TRIM_SPLIT_WHITESPACE, + style, + "using `str::trim()` or alike before `str::split_whitespace`" +} +declare_lint_pass!(TrimSplitWhitespace => [TRIM_SPLIT_WHITESPACE]); + +impl<'tcx> LateLintPass<'tcx> for TrimSplitWhitespace { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { + let tyckres = cx.typeck_results(); + if_chain! { + if let ExprKind::MethodCall(path, [split_recv], split_ws_span) = expr.kind; + if path.ident.name == sym!(split_whitespace); + if let Some(split_ws_def_id) = tyckres.type_dependent_def_id(expr.hir_id); + if cx.tcx.is_diagnostic_item(sym::str_split_whitespace, split_ws_def_id); + if let ExprKind::MethodCall(path, [_trim_recv], trim_span) = split_recv.kind; + if let trim_fn_name @ ("trim" | "trim_start" | "trim_end") = path.ident.name.as_str(); + if let Some(trim_def_id) = tyckres.type_dependent_def_id(split_recv.hir_id); + if is_one_of_trim_diagnostic_items(cx, trim_def_id); + then { + span_lint_and_sugg( + cx, + TRIM_SPLIT_WHITESPACE, + trim_span.with_hi(split_ws_span.lo()), + &format!("found call to `str::{}` before `str::split_whitespace`", trim_fn_name), + &format!("remove `{}()`", trim_fn_name), + String::new(), + Applicability::MachineApplicable, + ); + } + } + } +} + +fn is_one_of_trim_diagnostic_items(cx: &LateContext<'_>, trim_def_id: DefId) -> bool { + cx.tcx.is_diagnostic_item(sym::str_trim, trim_def_id) + || cx.tcx.is_diagnostic_item(sym::str_trim_start, trim_def_id) + || cx.tcx.is_diagnostic_item(sym::str_trim_end, trim_def_id) +} diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index b5dd27ff80d..c4c1aa11004 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -550,7 +550,7 @@ fn ident_difference_expr_with_base_location( // IdentIter, then the output of this function will be almost always be correct // in practice. // - // If it turns out that problematic cases are more prelavent than we assume, + // If it turns out that problematic cases are more prevalent than we assume, // then we should be able to change this function to do the correct traversal, // without needing to change the rest of the code. diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 3d1b2ee925b..78e388a49af 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -13,6 +13,7 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; +use std::fmt::Write as _; declare_clippy_lint! { /// ### What it does @@ -34,7 +35,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.38.0"] pub TYPE_REPETITION_IN_BOUNDS, - pedantic, + nursery, "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`" } @@ -64,7 +65,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.47.0"] pub TRAIT_DUPLICATION_IN_BOUNDS, - pedantic, + nursery, "Check if the same trait bounds are specified twice during a function declaration" } @@ -182,19 +183,19 @@ impl TraitBounds { for b in v.iter() { if let GenericBound::Trait(ref poly_trait_ref, _) = b { let path = &poly_trait_ref.trait_ref.path; - hint_string.push_str(&format!( + let _ = write!(hint_string, " {} +", snippet_with_applicability(cx, path.span, "..", &mut applicability) - )); + ); } } for b in p.bounds.iter() { if let GenericBound::Trait(ref poly_trait_ref, _) = b { let path = &poly_trait_ref.trait_ref.path; - hint_string.push_str(&format!( + let _ = write!(hint_string, " {} +", snippet_with_applicability(cx, path.span, "..", &mut applicability) - )); + ); } } hint_string.truncate(hint_string.len() - 2); @@ -241,7 +242,7 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { ); } else { - trait_resolutions_direct.push((res_where, span_where)) + trait_resolutions_direct.push((res_where, span_where)); } } } diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs index f359b606e45..0cbf5ccefa6 100644 --- a/clippy_lints/src/transmute/utils.rs +++ b/clippy_lints/src/transmute/utils.rs @@ -1,10 +1,9 @@ use clippy_utils::last_path_segment; use clippy_utils::source::snippet; -use clippy_utils::ty::is_normalizable; use if_chain::if_chain; use rustc_hir::{Expr, GenericArg, QPath, TyKind}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, cast::CastKind, Ty}; +use rustc_middle::ty::{cast::CastKind, Ty}; use rustc_span::DUMMY_SP; use rustc_typeck::check::{cast::CastCheck, FnCtxt, Inherited}; @@ -34,15 +33,12 @@ pub(super) fn get_type_snippet(cx: &LateContext<'_>, path: &QPath<'_>, to_ref_ty // check if the component types of the transmuted collection and the result have different ABI, // size or alignment pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool { - let empty_param_env = ty::ParamEnv::empty(); - // check if `from` and `to` are normalizable to avoid ICE (#4968) - if !(is_normalizable(cx, empty_param_env, from) && is_normalizable(cx, empty_param_env, to)) { - return false; - } - let from_ty_layout = cx.tcx.layout_of(empty_param_env.and(from)); - let to_ty_layout = cx.tcx.layout_of(empty_param_env.and(to)); - if let (Ok(from_layout), Ok(to_layout)) = (from_ty_layout, to_ty_layout) { - from_layout.size != to_layout.size || from_layout.align != to_layout.align || from_layout.abi != to_layout.abi + if let Ok(from) = cx.tcx.try_normalize_erasing_regions(cx.param_env, from) + && let Ok(to) = cx.tcx.try_normalize_erasing_regions(cx.param_env, to) + && let Ok(from_layout) = cx.tcx.layout_of(cx.param_env.and(from)) + && let Ok(to_layout) = cx.tcx.layout_of(cx.param_env.and(to)) + { + from_layout.size != to_layout.size || from_layout.align.abi != to_layout.align.abi } else { // no idea about layout, so don't lint false @@ -91,7 +87,7 @@ fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx> let res = check.do_check(&fn_ctxt); // do_check's documentation says that it might return Ok and create - // errors in the fcx instead of returing Err in some cases. Those cases + // errors in the fcx instead of returning Err in some cases. Those cases // should be filtered out before getting here. assert!( !fn_ctxt.errors_reported_since_creation(), diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 67cc8913318..353a6f6b899 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -432,8 +432,8 @@ impl Types { fn check_fn_decl(&mut self, cx: &LateContext<'_>, decl: &FnDecl<'_>, context: CheckTyContext) { // Ignore functions in trait implementations as they are usually forced by the trait definition. // - // FIXME: idially we would like to warn *if the compicated type can be simplified*, but it's hard to - // check. + // FIXME: ideally we would like to warn *if the complicated type can be simplified*, but it's hard + // to check. if context.is_in_trait_impl { return; } diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index c8912a18f18..465d8a914fb 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -156,8 +156,9 @@ fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> .array_windows::<2>() .rev() .map_while(|[start, end]| { - src.get(start.to_usize() - offset..end.to_usize() - offset) - .map(|text| (start.to_usize(), text.trim_start())) + let start = start.to_usize() - offset; + let end = end.to_usize() - offset; + src.get(start..end).map(|text| (start, text.trim_start())) }) .filter(|(_, text)| !text.is_empty()); @@ -182,7 +183,7 @@ fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> let (mut line_start, mut line) = (line_start, line); loop { if line.starts_with("/*") { - let src = src[line_start..line_starts.last().unwrap().to_usize()].trim_start(); + let src = src[line_start..line_starts.last().unwrap().to_usize() - offset].trim_start(); let mut tokens = tokenize(src); return src[..tokens.next().unwrap().len] .to_ascii_uppercase() diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index b25a6e3375b..f3f1f53aac5 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -1,18 +1,46 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_macro_callsite; +use clippy_utils::visitors::for_each_value_source; +use core::ops::ControlFlow; use rustc_errors::Applicability; -use rustc_hir::{Stmt, StmtKind}; +use rustc_hir::{Expr, ExprKind, PatKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::{self, Ty, TypeFoldable, TypeVisitor}; use super::LET_UNIT_VALUE; pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) { - if let StmtKind::Local(local) = stmt.kind { - if cx.typeck_results().pat_ty(local.pat).is_unit() { - if in_external_macro(cx.sess(), stmt.span) || local.pat.span.from_expansion() { - return; + if let StmtKind::Local(local) = stmt.kind + && let Some(init) = local.init + && !local.pat.span.from_expansion() + && !in_external_macro(cx.sess(), stmt.span) + && cx.typeck_results().pat_ty(local.pat).is_unit() + { + let needs_inferred = for_each_value_source(init, &mut |e| if needs_inferred_result_ty(cx, e) { + ControlFlow::Continue(()) + } else { + ControlFlow::Break(()) + }).is_continue(); + + if needs_inferred { + if !matches!(local.pat.kind, PatKind::Wild) { + span_lint_and_then( + cx, + LET_UNIT_VALUE, + stmt.span, + "this let-binding has unit value", + |diag| { + diag.span_suggestion( + local.pat.span, + "use a wild (`_`) binding", + "_", + Applicability::MaybeIncorrect, // snippet + ); + }, + ); } + } else { span_lint_and_then( cx, LET_UNIT_VALUE, @@ -33,3 +61,45 @@ pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) { } } } + +fn needs_inferred_result_ty(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { + let id = match e.kind { + ExprKind::Call( + Expr { + kind: ExprKind::Path(ref path), + hir_id, + .. + }, + _, + ) => cx.qpath_res(path, *hir_id).opt_def_id(), + ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(e.hir_id), + _ => return false, + }; + if let Some(id) = id + && let sig = cx.tcx.fn_sig(id).skip_binder() + && let ty::Param(output_ty) = *sig.output().kind() + { + sig.inputs().iter().all(|&ty| !ty_contains_param(ty, output_ty.index)) + } else { + false + } +} + +fn ty_contains_param(ty: Ty<'_>, index: u32) -> bool { + struct Visitor(u32); + impl<'tcx> TypeVisitor<'tcx> for Visitor { + type BreakTy = (); + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { + if let ty::Param(ty) = *ty.kind() { + if ty.index == self.0 { + ControlFlow::BREAK + } else { + ControlFlow::CONTINUE + } + } else { + ty.super_visit_with(self) + } + } + } + ty.visit_with(&mut Visitor(index)).is_break() +} diff --git a/clippy_lints/src/unit_types/mod.rs b/clippy_lints/src/unit_types/mod.rs index d9f5b53b413..a9e2073dec2 100644 --- a/clippy_lints/src/unit_types/mod.rs +++ b/clippy_lints/src/unit_types/mod.rs @@ -23,7 +23,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "pre 1.29.0"] pub LET_UNIT_VALUE, - pedantic, + style, "creating a `let` binding to a value of unit type, which usually can't be used afterwards" } diff --git a/clippy_lints/src/unnecessary_owned_empty_strings.rs b/clippy_lints/src/unnecessary_owned_empty_strings.rs new file mode 100644 index 00000000000..8a4f4c0ad97 --- /dev/null +++ b/clippy_lints/src/unnecessary_owned_empty_strings.rs @@ -0,0 +1,81 @@ +use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_diagnostic_item}; +use clippy_utils::{match_def_path, paths}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// + /// Detects cases of owned empty strings being passed as an argument to a function expecting `&str` + /// + /// ### Why is this bad? + /// + /// This results in longer and less readable code + /// + /// ### Example + /// ```rust + /// vec!["1", "2", "3"].join(&String::new()); + /// ``` + /// Use instead: + /// ```rust + /// vec!["1", "2", "3"].join(""); + /// ``` + #[clippy::version = "1.62.0"] + pub UNNECESSARY_OWNED_EMPTY_STRINGS, + style, + "detects cases of references to owned empty strings being passed as an argument to a function expecting `&str`" +} +declare_lint_pass!(UnnecessaryOwnedEmptyStrings => [UNNECESSARY_OWNED_EMPTY_STRINGS]); + +impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if_chain! { + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner_expr) = expr.kind; + if let ExprKind::Call(fun, args) = inner_expr.kind; + if let ExprKind::Path(ref qpath) = fun.kind; + if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); + if let ty::Ref(_, inner_str, _) = cx.typeck_results().expr_ty_adjusted(expr).kind(); + if inner_str.is_str(); + then { + if match_def_path(cx, fun_def_id, &paths::STRING_NEW) { + span_lint_and_sugg( + cx, + UNNECESSARY_OWNED_EMPTY_STRINGS, + expr.span, + "usage of `&String::new()` for a function expecting a `&str` argument", + "try", + "\"\"".to_owned(), + Applicability::MachineApplicable, + ); + } else { + if_chain! { + if match_def_path(cx, fun_def_id, &paths::FROM_FROM); + if let [.., last_arg] = args; + if let ExprKind::Lit(spanned) = &last_arg.kind; + if let LitKind::Str(symbol, _) = spanned.node; + if symbol.is_empty(); + let inner_expr_type = cx.typeck_results().expr_ty(inner_expr); + if is_type_diagnostic_item(cx, inner_expr_type, sym::String); + then { + span_lint_and_sugg( + cx, + UNNECESSARY_OWNED_EMPTY_STRINGS, + expr.span, + "usage of `&String::from(\"\")` for a function expecting a `&str` argument", + "try", + "\"\"".to_owned(), + Applicability::MachineApplicable, + ); + } + } + } + } + } + } +} diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index 1d4fe9cfd3c..ae431aac83b 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -5,7 +5,7 @@ use clippy_utils::diagnostics::span_lint_and_then; 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}; +use rustc_ast::{self as ast, Mutability, Pat, PatKind, PatKind::*, DUMMY_NODE_ID}; use rustc_ast_pretty::pprust; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -25,7 +25,7 @@ declare_clippy_lint! { /// *disjunctive normal form (DNF)* into *conjunctive normal form (CNF)*. /// /// ### Why is this bad? - /// In the example above, `Some` is repeated, which unncessarily complicates the pattern. + /// In the example above, `Some` is repeated, which unnecessarily complicates the pattern. /// /// ### Example /// ```rust @@ -230,6 +230,10 @@ fn transform_with_focus_on_idx(alternatives: &mut Vec>, focus_idx: usize) // with which a pattern `C(p_0)` may be formed, // which we would want to join with other `C(p_j)`s. Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_) + // Skip immutable refs, as grouping them saves few characters, + // and almost always requires adding parens (increasing noisiness). + // In the case of only two patterns, replacement adds net characters. + | Ref(_, Mutability::Not) // Dealt with elsewhere. | Or(_) | Paren(_) => false, // Transform `box x | ... | box y` into `box (x | y)`. @@ -241,10 +245,10 @@ fn transform_with_focus_on_idx(alternatives: &mut Vec>, focus_idx: usize) |k| matches!(k, Box(_)), |k| always_pat!(k, Box(p) => p), ), - // Transform `&m x | ... | &m y` into `&m (x | y)`. - Ref(target, m1) => extend_with_matching( + // Transform `&mut x | ... | &mut y` into `&mut (x | y)`. + Ref(target, Mutability::Mut) => extend_with_matching( target, start, alternatives, - |k| matches!(k, Ref(_, m2) if m1 == m2), // Mutabilities must match. + |k| matches!(k, Ref(_, Mutability::Mut)), |k| always_pat!(k, Ref(p, _) => p), ), // Transform `b @ p0 | ... b @ p1` into `b @ (p0 | p1)`. diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index f8e1021af0e..138f8bccb3f 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -30,7 +30,7 @@ declare_clippy_lint! { /// /// ### Known problems /// - Unaddressed false negative in fn bodies of trait implementations - /// - False positive with assotiated types in traits (#4140) + /// - False positive with associated types in traits (#4140) /// /// ### Example /// ```rust diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index d23c85c033b..ff5be825b78 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -70,7 +70,7 @@ macro_rules! bind { }; } -/// Transforms the given `Option` varibles into `OptionPat>`. +/// Transforms the given `Option` variables into `OptionPat>`. /// This displays as `Some($name)` or `None` when printed. The name of the inner binding /// is set to the name of the variable passed to the macro. macro_rules! opt_bind { diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 271c3a3dd18..74b0168a179 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -310,6 +310,12 @@ define_Conf! { /// the slice pattern that is suggested. If more elements would be necessary, the lint is suppressed. /// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements. (max_suggested_slice_pattern_length: u64 = 3), + /// Lint: AWAIT_HOLDING_INVALID_TYPE + (await_holding_invalid_types: Vec = Vec::new()), + /// Lint: LARGE_INCLUDE_FILE. + /// + /// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes + (max_include_file_size: u64 = 1_000_000), } /// Search for the configuration file. diff --git a/clippy_lints/src/utils/dump_hir.rs b/clippy_lints/src/utils/dump_hir.rs new file mode 100644 index 00000000000..01efc527a8c --- /dev/null +++ b/clippy_lints/src/utils/dump_hir.rs @@ -0,0 +1,55 @@ +use clippy_utils::get_attr; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// It formats the attached node with `{:#?}` and writes the result to the + /// standard output. This is intended for debugging. + /// + /// ### Examples + /// ```rs + /// #[clippy::dump] + /// use std::mem; + /// + /// #[clippy::dump] + /// fn foo(input: u32) -> u64 { + /// input as u64 + /// } + /// ``` + pub DUMP_HIR, + internal_warn, + "helper to dump info about code" +} + +declare_lint_pass!(DumpHir => [DUMP_HIR]); + +impl<'tcx> LateLintPass<'tcx> for DumpHir { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { + if has_attr(cx, item.hir_id()) { + println!("{item:#?}"); + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if has_attr(cx, expr.hir_id) { + println!("{expr:#?}"); + } + } + + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) { + match stmt.kind { + hir::StmtKind::Expr(e) | hir::StmtKind::Semi(e) if has_attr(cx, e.hir_id) => return, + _ => {}, + } + if has_attr(cx, stmt.hir_id) { + println!("{stmt:#?}"); + } + } +} + +fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool { + let attrs = cx.tcx.hir().attrs(hir_id); + get_attr(cx.sess(), attrs, "dump").count() > 0 +} diff --git a/clippy_lints/src/utils/inspector.rs b/clippy_lints/src/utils/inspector.rs deleted file mode 100644 index 37b114a0cfb..00000000000 --- a/clippy_lints/src/utils/inspector.rs +++ /dev/null @@ -1,577 +0,0 @@ -//! checks for attributes - -use clippy_utils::get_attr; -use rustc_ast::ast::{Attribute, InlineAsmTemplatePiece}; -use rustc_hir as hir; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty; -use rustc_session::Session; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -declare_clippy_lint! { - /// ### What it does - /// Dumps every ast/hir node which has the `#[clippy::dump]` - /// attribute - /// - /// ### Example - /// ```rust,ignore - /// #[clippy::dump] - /// extern crate foo; - /// ``` - /// - /// prints - /// - /// ```text - /// item `foo` - /// visibility inherited from outer item - /// extern crate dylib source: "/path/to/foo.so" - /// ``` - pub DEEP_CODE_INSPECTION, - internal_warn, - "helper to dump info about code" -} - -declare_lint_pass!(DeepCodeInspector => [DEEP_CODE_INSPECTION]); - -impl<'tcx> LateLintPass<'tcx> for DeepCodeInspector { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if !has_attr(cx.sess(), cx.tcx.hir().attrs(item.hir_id())) { - return; - } - print_item(cx, item); - } - - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { - if !has_attr(cx.sess(), cx.tcx.hir().attrs(item.hir_id())) { - return; - } - println!("impl item `{}`", item.ident.name); - match cx.tcx.visibility(item.def_id) { - ty::Visibility::Public => println!("public"), - ty::Visibility::Restricted(def_id) => { - if def_id.is_top_level_module() { - println!("visible crate wide") - } else { - println!("visible in module `{}`", cx.tcx.def_path_str(def_id)) - } - }, - ty::Visibility::Invisible => println!("invisible"), - } - match item.kind { - hir::ImplItemKind::Const(_, body_id) => { - println!("associated constant"); - print_expr(cx, &cx.tcx.hir().body(body_id).value, 1); - }, - hir::ImplItemKind::Fn(..) => println!("method"), - hir::ImplItemKind::TyAlias(_) => println!("associated type"), - } - } - - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if !has_attr(cx.sess(), cx.tcx.hir().attrs(expr.hir_id)) { - return; - } - print_expr(cx, expr, 0); - } - - fn check_arm(&mut self, cx: &LateContext<'tcx>, arm: &'tcx hir::Arm<'_>) { - if !has_attr(cx.sess(), cx.tcx.hir().attrs(arm.hir_id)) { - return; - } - print_pat(cx, arm.pat, 1); - if let Some(ref guard) = arm.guard { - println!("guard:"); - print_guard(cx, guard, 1); - } - println!("body:"); - print_expr(cx, arm.body, 1); - } - - fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) { - if !has_attr(cx.sess(), cx.tcx.hir().attrs(stmt.hir_id)) { - return; - } - match stmt.kind { - hir::StmtKind::Local(local) => { - println!("local variable of type {}", cx.typeck_results().node_type(local.hir_id)); - println!("pattern:"); - print_pat(cx, local.pat, 0); - if let Some(e) = local.init { - println!("init expression:"); - print_expr(cx, e, 0); - } - }, - hir::StmtKind::Item(_) => println!("item decl"), - hir::StmtKind::Expr(e) | hir::StmtKind::Semi(e) => print_expr(cx, e, 0), - } - } -} - -fn has_attr(sess: &Session, attrs: &[Attribute]) -> bool { - get_attr(sess, attrs, "dump").count() > 0 -} - -#[allow(clippy::similar_names)] -#[allow(clippy::too_many_lines)] -fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { - let ind = " ".repeat(indent); - println!("{}+", ind); - println!("{}ty: {}", ind, cx.typeck_results().expr_ty(expr)); - println!( - "{}adjustments: {:?}", - ind, - cx.typeck_results().adjustments().get(expr.hir_id) - ); - match expr.kind { - hir::ExprKind::Box(e) => { - println!("{}Box", ind); - print_expr(cx, e, indent + 1); - }, - hir::ExprKind::Array(v) => { - println!("{}Array", ind); - for e in v { - print_expr(cx, e, indent + 1); - } - }, - hir::ExprKind::Call(func, args) => { - println!("{}Call", ind); - println!("{}function:", ind); - print_expr(cx, func, indent + 1); - println!("{}arguments:", ind); - for arg in args { - print_expr(cx, arg, indent + 1); - } - }, - hir::ExprKind::Let(hir::Let { pat, init, ty, .. }) => { - print_pat(cx, pat, indent + 1); - if let Some(ty) = ty { - println!("{} type annotation: {:?}", ind, ty); - } - print_expr(cx, init, indent + 1); - }, - hir::ExprKind::MethodCall(path, args, _) => { - println!("{}MethodCall", ind); - println!("{}method name: {}", ind, path.ident.name); - for arg in args { - print_expr(cx, arg, indent + 1); - } - }, - hir::ExprKind::Tup(v) => { - println!("{}Tup", ind); - for e in v { - print_expr(cx, e, indent + 1); - } - }, - hir::ExprKind::Binary(op, lhs, rhs) => { - println!("{}Binary", ind); - println!("{}op: {:?}", ind, op.node); - println!("{}lhs:", ind); - print_expr(cx, lhs, indent + 1); - println!("{}rhs:", ind); - print_expr(cx, rhs, indent + 1); - }, - hir::ExprKind::Unary(op, inner) => { - println!("{}Unary", ind); - println!("{}op: {:?}", ind, op); - print_expr(cx, inner, indent + 1); - }, - hir::ExprKind::Lit(ref lit) => { - println!("{}Lit", ind); - println!("{}{:?}", ind, lit); - }, - hir::ExprKind::Cast(e, target) => { - println!("{}Cast", ind); - print_expr(cx, e, indent + 1); - println!("{}target type: {:?}", ind, target); - }, - hir::ExprKind::Type(e, target) => { - println!("{}Type", ind); - print_expr(cx, e, indent + 1); - println!("{}target type: {:?}", ind, target); - }, - hir::ExprKind::Loop(..) => { - println!("{}Loop", ind); - }, - hir::ExprKind::If(cond, _, ref else_opt) => { - println!("{}If", ind); - println!("{}condition:", ind); - print_expr(cx, cond, indent + 1); - if let Some(els) = *else_opt { - println!("{}else:", ind); - print_expr(cx, els, indent + 1); - } - }, - hir::ExprKind::Match(cond, _, ref source) => { - println!("{}Match", ind); - println!("{}condition:", ind); - print_expr(cx, cond, indent + 1); - println!("{}source: {:?}", ind, source); - }, - hir::ExprKind::Closure(ref clause, _, _, _, _) => { - println!("{}Closure", ind); - println!("{}clause: {:?}", ind, clause); - }, - hir::ExprKind::Yield(sub, _) => { - println!("{}Yield", ind); - print_expr(cx, sub, indent + 1); - }, - hir::ExprKind::Block(_, _) => { - println!("{}Block", ind); - }, - hir::ExprKind::Assign(lhs, rhs, _) => { - println!("{}Assign", ind); - println!("{}lhs:", ind); - print_expr(cx, lhs, indent + 1); - println!("{}rhs:", ind); - print_expr(cx, rhs, indent + 1); - }, - hir::ExprKind::AssignOp(ref binop, lhs, rhs) => { - println!("{}AssignOp", ind); - println!("{}op: {:?}", ind, binop.node); - println!("{}lhs:", ind); - print_expr(cx, lhs, indent + 1); - println!("{}rhs:", ind); - print_expr(cx, rhs, indent + 1); - }, - hir::ExprKind::Field(e, ident) => { - println!("{}Field", ind); - println!("{}field name: {}", ind, ident.name); - println!("{}struct expr:", ind); - print_expr(cx, e, indent + 1); - }, - hir::ExprKind::Index(arr, idx) => { - println!("{}Index", ind); - println!("{}array expr:", ind); - print_expr(cx, arr, indent + 1); - println!("{}index expr:", ind); - print_expr(cx, idx, indent + 1); - }, - hir::ExprKind::Path(hir::QPath::Resolved(ref ty, path)) => { - println!("{}Resolved Path, {:?}", ind, ty); - println!("{}path: {:?}", ind, path); - }, - hir::ExprKind::Path(hir::QPath::TypeRelative(ty, seg)) => { - println!("{}Relative Path, {:?}", ind, ty); - println!("{}seg: {:?}", ind, seg); - }, - hir::ExprKind::Path(hir::QPath::LangItem(lang_item, ..)) => { - println!("{}Lang Item Path, {:?}", ind, lang_item.name()); - }, - hir::ExprKind::AddrOf(kind, ref muta, e) => { - println!("{}AddrOf", ind); - println!("kind: {:?}", kind); - println!("mutability: {:?}", muta); - print_expr(cx, e, indent + 1); - }, - hir::ExprKind::Break(_, ref e) => { - println!("{}Break", ind); - if let Some(e) = *e { - print_expr(cx, e, indent + 1); - } - }, - hir::ExprKind::Continue(_) => println!("{}Again", ind), - hir::ExprKind::Ret(ref e) => { - println!("{}Ret", ind); - if let Some(e) = *e { - print_expr(cx, e, indent + 1); - } - }, - hir::ExprKind::InlineAsm(asm) => { - println!("{}InlineAsm", ind); - println!("{}template: {}", ind, InlineAsmTemplatePiece::to_string(asm.template)); - println!("{}options: {:?}", ind, asm.options); - println!("{}operands:", ind); - for (op, _op_sp) in asm.operands { - match op { - hir::InlineAsmOperand::In { expr, .. } - | hir::InlineAsmOperand::InOut { expr, .. } => { - print_expr(cx, expr, indent + 1); - } - hir::InlineAsmOperand::Out { expr, .. } => { - if let Some(expr) = expr { - print_expr(cx, expr, indent + 1); - } - }, - hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { - print_expr(cx, in_expr, indent + 1); - if let Some(out_expr) = out_expr { - print_expr(cx, out_expr, indent + 1); - } - }, - hir::InlineAsmOperand::Const { anon_const } - | hir::InlineAsmOperand::SymFn { anon_const } => { - println!("{}anon_const:", ind); - print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1); - }, - hir::InlineAsmOperand::SymStatic { path, .. } => { - match path { - hir::QPath::Resolved(ref ty, path) => { - println!("{}Resolved Path, {:?}", ind, ty); - println!("{}path: {:?}", ind, path); - }, - hir::QPath::TypeRelative(ty, seg) => { - println!("{}Relative Path, {:?}", ind, ty); - println!("{}seg: {:?}", ind, seg); - }, - hir::QPath::LangItem(lang_item, ..) => { - println!("{}Lang Item Path, {:?}", ind, lang_item.name()); - }, - } - } - } - } - }, - hir::ExprKind::Struct(path, fields, ref base) => { - println!("{}Struct", ind); - println!("{}path: {:?}", ind, path); - for field in fields { - println!("{}field \"{}\":", ind, field.ident.name); - print_expr(cx, field.expr, indent + 1); - } - if let Some(base) = *base { - println!("{}base:", ind); - print_expr(cx, base, indent + 1); - } - }, - hir::ExprKind::ConstBlock(ref anon_const) => { - println!("{}ConstBlock", ind); - println!("{}anon_const:", ind); - print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1); - }, - hir::ExprKind::Repeat(val, length) => { - println!("{}Repeat", ind); - println!("{}value:", ind); - print_expr(cx, val, indent + 1); - println!("{}repeat count:", ind); - match length { - hir::ArrayLen::Infer(_, _) => println!("{}repeat count: _", ind), - hir::ArrayLen::Body(anon_const) => { - print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1); - }, - } - }, - hir::ExprKind::Err => { - println!("{}Err", ind); - }, - hir::ExprKind::DropTemps(e) => { - println!("{}DropTemps", ind); - print_expr(cx, e, indent + 1); - }, - } -} - -fn print_item(cx: &LateContext<'_>, item: &hir::Item<'_>) { - let did = item.def_id; - println!("item `{}`", item.ident.name); - match cx.tcx.visibility(item.def_id) { - ty::Visibility::Public => println!("public"), - ty::Visibility::Restricted(def_id) => { - if def_id.is_top_level_module() { - println!("visible crate wide") - } else { - println!("visible in module `{}`", cx.tcx.def_path_str(def_id)) - } - }, - ty::Visibility::Invisible => println!("invisible"), - } - match item.kind { - hir::ItemKind::ExternCrate(ref _renamed_from) => { - if let Some(crate_id) = cx.tcx.extern_mod_stmt_cnum(did) { - let source = cx.tcx.used_crate_source(crate_id); - if let Some(ref src) = source.dylib { - println!("extern crate dylib source: {:?}", src.0); - } - if let Some(ref src) = source.rlib { - println!("extern crate rlib source: {:?}", src.0); - } - } else { - println!("weird extern crate without a crate id"); - } - }, - hir::ItemKind::Use(path, ref kind) => println!("{:?}, {:?}", path, kind), - hir::ItemKind::Static(..) => println!("static item of type {:#?}", cx.tcx.type_of(did)), - hir::ItemKind::Const(..) => println!("const item of type {:#?}", cx.tcx.type_of(did)), - hir::ItemKind::Fn(..) => { - let item_ty = cx.tcx.type_of(did); - println!("function of type {:#?}", item_ty); - }, - hir::ItemKind::Macro(ref macro_def, _) => { - if macro_def.macro_rules { - println!("macro introduced by `macro_rules!`"); - } else { - println!("macro introduced by `macro`"); - } - }, - hir::ItemKind::Mod(..) => println!("module"), - hir::ItemKind::ForeignMod { abi, .. } => println!("foreign module with abi: {}", abi), - hir::ItemKind::GlobalAsm(asm) => println!("global asm: {:?}", asm), - hir::ItemKind::TyAlias(..) => { - println!("type alias for {:?}", cx.tcx.type_of(did)); - }, - hir::ItemKind::OpaqueTy(..) => { - println!("existential type with real type {:?}", cx.tcx.type_of(did)); - }, - hir::ItemKind::Enum(..) => { - println!("enum definition of type {:?}", cx.tcx.type_of(did)); - }, - hir::ItemKind::Struct(..) => { - println!("struct definition of type {:?}", cx.tcx.type_of(did)); - }, - hir::ItemKind::Union(..) => { - println!("union definition of type {:?}", cx.tcx.type_of(did)); - }, - hir::ItemKind::Trait(..) => { - println!("trait decl"); - if cx.tcx.trait_is_auto(did.to_def_id()) { - println!("trait is auto"); - } else { - println!("trait is not auto"); - } - }, - hir::ItemKind::TraitAlias(..) => { - println!("trait alias"); - }, - hir::ItemKind::Impl(hir::Impl { - of_trait: Some(ref _trait_ref), - .. - }) => { - println!("trait impl"); - }, - hir::ItemKind::Impl(hir::Impl { of_trait: None, .. }) => { - println!("impl"); - }, - } -} - -#[allow(clippy::similar_names)] -#[allow(clippy::too_many_lines)] -fn print_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, indent: usize) { - let ind = " ".repeat(indent); - println!("{}+", ind); - match pat.kind { - hir::PatKind::Wild => println!("{}Wild", ind), - hir::PatKind::Binding(ref mode, .., ident, ref inner) => { - println!("{}Binding", ind); - println!("{}mode: {:?}", ind, mode); - println!("{}name: {}", ind, ident.name); - if let Some(inner) = *inner { - println!("{}inner:", ind); - print_pat(cx, inner, indent + 1); - } - }, - hir::PatKind::Or(fields) => { - println!("{}Or", ind); - for field in fields { - print_pat(cx, field, indent + 1); - } - }, - hir::PatKind::Struct(ref path, fields, ignore) => { - println!("{}Struct", ind); - println!( - "{}name: {}", - ind, - rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)) - ); - println!("{}ignore leftover fields: {}", ind, ignore); - println!("{}fields:", ind); - for field in fields { - println!("{} field name: {}", ind, field.ident.name); - if field.is_shorthand { - println!("{} in shorthand notation", ind); - } - print_pat(cx, field.pat, indent + 1); - } - }, - hir::PatKind::TupleStruct(ref path, fields, opt_dots_position) => { - println!("{}TupleStruct", ind); - println!( - "{}path: {}", - ind, - rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)) - ); - if let Some(dot_position) = opt_dots_position { - println!("{}dot position: {}", ind, dot_position); - } - for field in fields { - print_pat(cx, field, indent + 1); - } - }, - hir::PatKind::Path(hir::QPath::Resolved(ref ty, path)) => { - println!("{}Resolved Path, {:?}", ind, ty); - println!("{}path: {:?}", ind, path); - }, - hir::PatKind::Path(hir::QPath::TypeRelative(ty, seg)) => { - println!("{}Relative Path, {:?}", ind, ty); - println!("{}seg: {:?}", ind, seg); - }, - hir::PatKind::Path(hir::QPath::LangItem(lang_item, ..)) => { - println!("{}Lang Item Path, {:?}", ind, lang_item.name()); - }, - hir::PatKind::Tuple(pats, opt_dots_position) => { - println!("{}Tuple", ind); - if let Some(dot_position) = opt_dots_position { - println!("{}dot position: {}", ind, dot_position); - } - for field in pats { - print_pat(cx, field, indent + 1); - } - }, - hir::PatKind::Box(inner) => { - println!("{}Box", ind); - print_pat(cx, inner, indent + 1); - }, - hir::PatKind::Ref(inner, ref muta) => { - println!("{}Ref", ind); - println!("{}mutability: {:?}", ind, muta); - print_pat(cx, inner, indent + 1); - }, - hir::PatKind::Lit(e) => { - println!("{}Lit", ind); - print_expr(cx, e, indent + 1); - }, - hir::PatKind::Range(ref l, ref r, ref range_end) => { - println!("{}Range", ind); - if let Some(expr) = l { - print_expr(cx, expr, indent + 1); - } - if let Some(expr) = r { - print_expr(cx, expr, indent + 1); - } - match *range_end { - hir::RangeEnd::Included => println!("{} end included", ind), - hir::RangeEnd::Excluded => println!("{} end excluded", ind), - } - }, - hir::PatKind::Slice(first_pats, ref range, last_pats) => { - println!("{}Slice [a, b, ..i, y, z]", ind); - println!("[a, b]:"); - for pat in first_pats { - print_pat(cx, pat, indent + 1); - } - println!("i:"); - if let Some(pat) = *range { - print_pat(cx, pat, indent + 1); - } - println!("[y, z]:"); - for pat in last_pats { - print_pat(cx, pat, indent + 1); - } - }, - } -} - -fn print_guard(cx: &LateContext<'_>, guard: &hir::Guard<'_>, indent: usize) { - let ind = " ".repeat(indent); - println!("{}+", ind); - match guard { - hir::Guard::If(expr) => { - println!("{}If", ind); - print_expr(cx, expr, indent + 1); - }, - hir::Guard::IfLet(pat, expr) => { - println!("{}IfLet", ind); - print_pat(cx, pat, indent + 1); - print_expr(cx, expr, indent + 1); - }, - } -} diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 25d74b8c499..0e8f40e9210 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -292,7 +292,7 @@ declare_clippy_lint! { /// Checks for unnecessary conversion from Symbol to a string. /// /// ### Why is this bad? - /// It's faster use symbols directly intead of strings. + /// It's faster use symbols directly instead of strings. /// /// ### Example /// Bad: @@ -823,7 +823,7 @@ fn suggest_note( cx, COLLAPSIBLE_SPAN_LINT_CALLS, expr.span, - "this call is collspible", + "this call is collapsible", "collapse into", format!( "span_lint_and_note({}, {}, {}, {}, {}, {})", diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index ca03b8010dd..8c1910b3b2a 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 std::path::Path; /// This is the output file of the lint collector. const OUTPUT_FILE: &str = "../util/gh-pages/lints.json"; /// These lints are excluded from the export. -const BLACK_LISTED_LINTS: [&str; 3] = ["lint_author", "deep_code_inspection", "internal_metadata_collector"]; +const BLACK_LISTED_LINTS: &[&str] = &["lint_author", "dump_hir", "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"]; @@ -117,7 +117,7 @@ const APPLICABILITY_NAME_INDEX: usize = 2; /// This applicability will be set for unresolved applicability values. const APPLICABILITY_UNRESOLVED_STR: &str = "Unresolved"; /// The version that will be displayed if none has been defined -const VERION_DEFAULT_STR: &str = "Unknown"; +const VERSION_DEFAULT_STR: &str = "Unknown"; declare_clippy_lint! { /// ### What it does @@ -571,7 +571,7 @@ fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option { fn get_lint_version(cx: &LateContext<'_>, item: &Item<'_>) -> String { extract_clippy_version_value(cx, item).map_or_else( - || VERION_DEFAULT_STR.to_string(), + || VERSION_DEFAULT_STR.to_string(), |version| version.as_str().to_string(), ) } @@ -872,7 +872,7 @@ impl<'a, 'hir> IsMultiSpanScanner<'a, 'hir> { self.suggestion_count += 2; } - /// Checks if the suggestions include multiple spanns + /// Checks if the suggestions include multiple spans fn is_multi_part(&self) -> bool { self.suggestion_count > 1 } diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index dc385ebacba..787e9fd982c 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; +pub mod dump_hir; #[cfg(feature = "internal")] pub mod internal_lints; diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index be46b791aa4..fdb822c3e5b 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -5,7 +5,7 @@ use if_chain::if_chain; use rustc_ast::ast::{self, LitFloatType, LitKind}; use rustc_data_structures::sync::Lrc; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, QPath, UnOp}; +use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::mir::interpret::Scalar; use rustc_middle::ty::subst::{Subst, SubstsRef}; @@ -400,6 +400,22 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { let res = self.typeck_results.qpath_res(qpath, id); match res { Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => { + // Check if this constant is based on `cfg!(..)`, + // which is NOT constant for our purposes. + if let Some(node) = self.lcx.tcx.hir().get_if_local(def_id) && + let Node::Item(&Item { + kind: ItemKind::Const(_, body_id), + .. + }) = node && + let Node::Expr(&Expr { + kind: ExprKind::Lit(_), + span, + .. + }) = self.lcx.tcx.hir().get(body_id.hir_id) && + is_direct_expn_of(span, "cfg").is_some() { + return None; + } + let substs = self.typeck_results.node_substs(id); let substs = if self.substs.is_empty() { substs diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index c05317f59b7..f4da625f1e3 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -1,4 +1,4 @@ -use crate::consts::{constant_context, constant_simple}; +use crate::consts::constant_simple; use crate::source::snippet_opt; use rustc_ast::ast::InlineAsmTemplatePiece; use rustc_data_structures::fx::FxHasher; @@ -16,15 +16,14 @@ use rustc_span::Symbol; use std::hash::{Hash, Hasher}; /// Type used to check whether two ast are the same. This is different from the -/// operator -/// `==` on ast types as this operator would compare true equality with ID and -/// span. +/// operator `==` on ast types as this operator would compare true equality with +/// ID and span. /// /// Note that some expressions kinds are not considered but could be added. pub struct SpanlessEq<'a, 'tcx> { /// Context used to evaluate constant expressions. cx: &'a LateContext<'tcx>, - maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>, + maybe_typeck_results: Option<(&'tcx TypeckResults<'tcx>, &'tcx TypeckResults<'tcx>)>, allow_side_effects: bool, expr_fallback: Option, &Expr<'_>) -> bool + 'a>>, } @@ -33,7 +32,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { pub fn new(cx: &'a LateContext<'tcx>) -> Self { Self { cx, - maybe_typeck_results: cx.maybe_typeck_results(), + maybe_typeck_results: cx.maybe_typeck_results().map(|x| (x, x)), allow_side_effects: true, expr_fallback: None, } @@ -102,9 +101,9 @@ impl HirEqInterExpr<'_, '_, '_> { (&StmtKind::Local(l), &StmtKind::Local(r)) => { // This additional check ensures that the type of the locals are equivalent even if the init // expression or type have some inferred parts. - if let Some(typeck) = self.inner.maybe_typeck_results { - let l_ty = typeck.pat_ty(l.pat); - let r_ty = typeck.pat_ty(r.pat); + if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results { + let l_ty = typeck_lhs.pat_ty(l.pat); + let r_ty = typeck_rhs.pat_ty(r.pat); if l_ty != r_ty { return false; } @@ -182,9 +181,17 @@ impl HirEqInterExpr<'_, '_, '_> { } pub fn eq_body(&mut self, left: BodyId, right: BodyId) -> bool { - let cx = self.inner.cx; - let eval_const = |body| constant_context(cx, cx.tcx.typeck_body(body)).expr(&cx.tcx.hir().body(body).value); - eval_const(left) == eval_const(right) + // swap out TypeckResults when hashing a body + let old_maybe_typeck_results = self.inner.maybe_typeck_results.replace(( + self.inner.cx.tcx.typeck_body(left), + self.inner.cx.tcx.typeck_body(right), + )); + let res = self.eq_expr( + &self.inner.cx.tcx.hir().body(left).value, + &self.inner.cx.tcx.hir().body(right).value, + ); + self.inner.maybe_typeck_results = old_maybe_typeck_results; + res } #[allow(clippy::similar_names)] @@ -193,10 +200,10 @@ impl HirEqInterExpr<'_, '_, '_> { return false; } - if let Some(typeck_results) = self.inner.maybe_typeck_results { + if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results { if let (Some(l), Some(r)) = ( - constant_simple(self.inner.cx, typeck_results, left), - constant_simple(self.inner.cx, typeck_results, right), + constant_simple(self.inner.cx, typeck_lhs, left), + constant_simple(self.inner.cx, typeck_rhs, right), ) { if l == r { return true; @@ -674,8 +681,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_expr(out_expr); } }, - InlineAsmOperand::Const { anon_const } => self.hash_body(anon_const.body), - InlineAsmOperand::SymFn { anon_const } => self.hash_body(anon_const.body), + InlineAsmOperand::Const { anon_const } | InlineAsmOperand::SymFn { anon_const } => { + self.hash_body(anon_const.body); + }, InlineAsmOperand::SymStatic { path, def_id: _ } => self.hash_qpath(path), } } diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index e7d4c5a4952..a268e339bb1 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -367,7 +367,7 @@ impl<'tcx> FormatArgsExpn<'tcx> { expr_visitor_no_bodies(|e| { // if we're still inside of the macro definition... if e.span.ctxt() == expr.span.ctxt() { - // ArgumnetV1::new_() + // ArgumentV1::new_() if_chain! { if let ExprKind::Call(callee, [val]) = e.kind; if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind; diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 0424e067202..134fd1ce505 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -32,4 +32,5 @@ msrv_aliases! { 1,28,0 { FROM_BOOL } 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR } 1,16,0 { STR_REPEAT } + 1,24,0 { IS_ASCII_DIGIT } } diff --git a/clippy_utils/src/numeric_literal.rs b/clippy_utils/src/numeric_literal.rs index 908ff822712..b92d42e8323 100644 --- a/clippy_utils/src/numeric_literal.rs +++ b/clippy_utils/src/numeric_literal.rs @@ -57,7 +57,7 @@ impl<'a> NumericLiteral<'a> { .trim_start() .chars() .next() - .map_or(false, |c| c.is_digit(10)) + .map_or(false, |c| c.is_ascii_digit()) { let (unsuffixed, suffix) = split_suffix(src, lit_kind); let float = matches!(lit_kind, LitKind::Float(..)); diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index e5fa6deefc5..60971fb716d 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -61,6 +61,7 @@ pub const IO_READ: [&str; 3] = ["std", "io", "Read"]; pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"]; pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"]; pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"]; +pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"]; pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"]; #[allow(clippy::invalid_paths)] // internal lints do not know about all external crates pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"]; @@ -148,6 +149,8 @@ pub const CONVERT_IDENTITY: [&str; 3] = ["core", "convert", "identity"]; pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"]; pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"]; pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"]; +pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"]; +pub const STR_BYTES: [&str; 4] = ["core", "str", "", "bytes"]; pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "", "ends_with"]; pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"]; pub const STR_LEN: [&str; 4] = ["core", "str", "", "len"]; diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index fe411220484..75808b1b174 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -211,8 +211,9 @@ fn check_statement<'tcx>( StatementKind::FakeRead(box (_, place)) => check_place(tcx, *place, span, body), // just an assignment - StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => - check_place(tcx, **place, span, body), + StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => { + check_place(tcx, **place, span, body) + }, StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping { dst, src, count }) => { check_operand(tcx, dst, span, body)?; diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index dbad607c58e..c69a3d8d2a1 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -7,9 +7,28 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LintContext}; use rustc_span::hygiene; +use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, Pos, Span, SyntaxContext}; use std::borrow::Cow; +/// Checks if the span starts with the given text. This will return false if the span crosses +/// multiple files or if source is not available. +/// +/// This is used to check for proc macros giving unhelpful spans to things. +pub fn span_starts_with(cx: &T, span: Span, text: &str) -> bool { + fn helper(sm: &SourceMap, span: Span, text: &str) -> bool { + let pos = sm.lookup_byte_offset(span.lo()); + let Some(ref src) = pos.sf.src else { + return false; + }; + let end = span.hi() - pos.sf.start_pos; + src.get(pos.pos.0 as usize..end.0 as usize) + // Expression spans can include wrapping parenthesis. Remove them first. + .map_or(false, |s| s.trim_start_matches('(').starts_with(text)) + } + helper(cx.sess().source_map(), span, text) +} + /// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`. /// Also takes an `Option` which can be put inside the braces. pub fn expr_block<'a, T: LintContext>( @@ -89,7 +108,7 @@ pub fn is_present_in_source(cx: &T, span: Span) -> bool { true } -/// Returns the positon just before rarrow +/// Returns the position just before rarrow /// /// ```rust,ignore /// fn into(self) -> () {} diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 794d2e1026f..18915553e61 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -18,7 +18,7 @@ use rustc_span::source_map::{BytePos, CharPos, Pos, Span, SyntaxContext}; use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use std::borrow::Cow; use std::convert::TryInto; -use std::fmt::Display; +use std::fmt::{Display, Write as _}; use std::iter; use std::ops::{Add, Neg, Not, Sub}; @@ -902,7 +902,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { if cmt.place.projections.is_empty() { // handle item without any projection, that needs an explicit borrowing // i.e.: suggest `&x` instead of `x` - self.suggestion_start.push_str(&format!("{}&{}", start_snip, ident_str)); + let _ = write!(self.suggestion_start, "{}&{}", start_snip, ident_str); } else { // cases where a parent `Call` or `MethodCall` is using the item // i.e.: suggest `.contains(&x)` for `.find(|x| [1, 2, 3].contains(x)).is_none()` @@ -917,8 +917,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { // given expression is the self argument and will be handled completely by the compiler // i.e.: `|x| x.is_something()` ExprKind::MethodCall(_, [self_expr, ..], _) if self_expr.hir_id == cmt.hir_id => { - self.suggestion_start - .push_str(&format!("{}{}", start_snip, ident_str_with_proj)); + let _ = write!(self.suggestion_start, "{}{}", start_snip, ident_str_with_proj); self.next_pos = span.hi(); return; }, @@ -1026,8 +1025,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { } } - self.suggestion_start - .push_str(&format!("{}{}", start_snip, replacement_str)); + let _ = write!(self.suggestion_start, "{}{}", start_snip, replacement_str); } self.next_pos = span.hi(); } diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index e3fc76f4e1a..901e3e5390c 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -3,11 +3,11 @@ #![allow(clippy::module_name_repetitions)] use rustc_ast::ast::Mutability; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{Expr, TyKind, Unsafety}; +use rustc_hir::{Expr, LangItem, TyKind, Unsafety}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; @@ -22,7 +22,7 @@ use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::query::normalize::AtExt; use std::iter; -use crate::{match_def_path, must_use_attr, path_res}; +use crate::{match_def_path, must_use_attr, path_res, paths}; // Checks if the given type implements copy. pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { @@ -83,6 +83,20 @@ pub fn get_associated_type<'tcx>( }) } +/// Get the diagnostic name of a type, e.g. `sym::HashMap`. To check if a type +/// implements a trait marked with a diagnostic item use [`implements_trait`]. +/// +/// For a further exploitation what diagnostic items are see [diagnostic items] in +/// rustc-dev-guide. +/// +/// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html +pub fn get_type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option { + match ty.kind() { + ty::Adt(adt, _) => cx.tcx.get_diagnostic_name(adt.did()), + _ => None, + } +} + /// 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` @@ -319,6 +333,57 @@ pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool { } } +/// 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. +pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + fn needs_ordered_drop_inner<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet>) -> bool { + if !seen.insert(ty) { + return false; + } + if !ty.has_significant_drop(cx.tcx, cx.param_env) { + false + } + // Check for std types which implement drop, but only for memory allocation. + else if is_type_lang_item(cx, ty, LangItem::OwnedBox) + || matches!( + get_type_diagnostic_name(cx, ty), + Some(sym::HashSet | sym::Rc | sym::Arc | sym::cstring_type) + ) + || 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| needs_ordered_drop_inner(cx, ty, seen)) + } else { + true + } + } 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(fields) => fields.iter().any(|ty| needs_ordered_drop_inner(cx, ty, seen)), + ty::Array(ty, _) => needs_ordered_drop_inner(cx, *ty, seen), + ty::Adt(adt, subs) => adt + .all_fields() + .map(|f| f.ty(cx.tcx, subs)) + .any(|ty| needs_ordered_drop_inner(cx, ty, seen)), + _ => true, + } + } else { + true + } + } + + needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default()) +} + /// Peels off all references on the type. Returns the underlying type and the number of references /// removed. pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) { diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index 405e306359b..4236e3aae2f 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -3,7 +3,7 @@ use crate::visitors::{expr_visitor, expr_visitor_no_bodies}; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::HirIdSet; -use rustc_hir::{Expr, ExprKind, HirId}; +use rustc_hir::{Expr, ExprKind, HirId, Node}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; @@ -169,6 +169,32 @@ pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool { pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr<'_>) -> bool { let Some(block) = utils::get_enclosing_block(cx, local_id) else { return false }; + + // for _ in 1..3 { + // local + // } + // + // let closure = || local; + // closure(); + // closure(); + let in_loop_or_closure = cx + .tcx + .hir() + .parent_iter(after.hir_id) + .take_while(|&(id, _)| id != block.hir_id) + .any(|(_, node)| { + matches!( + node, + Node::Expr(Expr { + kind: ExprKind::Loop(..) | ExprKind::Closure(..), + .. + }) + ) + }); + if in_loop_or_closure { + return true; + } + let mut used_after_expr = false; let mut past_expr = false; expr_visitor(cx, |expr| { @@ -178,7 +204,10 @@ pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr if expr.hir_id == after.hir_id { past_expr = true; - } else if past_expr && utils::path_to_local_id(expr, local_id) { + return false; + } + + if past_expr && utils::path_to_local_id(expr, local_id) { used_after_expr = true; } !used_after_expr diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index 40451b17a9c..c00bc2bd213 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -1,9 +1,11 @@ use crate::path_to_local_id; +use core::ops::ControlFlow; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor}; use rustc_hir::{ - Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Stmt, UnOp, Unsafety, + Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Stmt, UnOp, UnsafeSource, + Unsafety, }; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; @@ -370,3 +372,67 @@ pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { v.visit_expr(e); v.is_unsafe } + +/// Checks if the given expression contains an unsafe block +pub fn contains_unsafe_block<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool { + struct V<'cx, 'tcx> { + cx: &'cx LateContext<'tcx>, + found_unsafe: bool, + } + impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> { + type NestedFilter = nested_filter::OnlyBodies; + fn nested_visit_map(&mut self) -> Self::Map { + self.cx.tcx.hir() + } + + fn visit_block(&mut self, b: &'tcx Block<'_>) { + if self.found_unsafe { + return; + } + if b.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) { + self.found_unsafe = true; + return; + } + walk_block(self, b); + } + } + let mut v = V { + cx, + found_unsafe: false, + }; + v.visit_expr(e); + v.found_unsafe +} + +/// Runs the given function for each sub-expression producing the final value consumed by the parent +/// of the give expression. +/// +/// e.g. for the following expression +/// ```rust,ignore +/// if foo { +/// f(0) +/// } else { +/// 1 + 1 +/// } +/// ``` +/// this will pass both `f(0)` and `1+1` to the given function. +pub fn for_each_value_source<'tcx, B>( + e: &'tcx Expr<'tcx>, + f: &mut impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow, +) -> ControlFlow { + match e.kind { + ExprKind::Block(Block { expr: Some(e), .. }, _) => for_each_value_source(e, f), + ExprKind::Match(_, arms, _) => { + for arm in arms { + for_each_value_source(arm.body, f)?; + } + ControlFlow::Continue(()) + }, + ExprKind::If(_, if_expr, Some(else_expr)) => { + for_each_value_source(if_expr, f)?; + for_each_value_source(else_expr, f) + }, + ExprKind::DropTemps(e) => for_each_value_source(e, f), + _ => f(e), + } +} diff --git a/doc/adding_lints.md b/doc/adding_lints.md index cf16a1d5d3d..307cf2f3a90 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -22,6 +22,7 @@ because that's clearly a non-descriptive name. - [Adding the lint logic](#adding-the-lint-logic) - [Specifying the lint's minimum supported Rust version (MSRV)](#specifying-the-lints-minimum-supported-rust-version-msrv) - [Author lint](#author-lint) + - [Print HIR lint](#print-hir-lint) - [Documentation](#documentation) - [Running rustfmt](#running-rustfmt) - [Debugging](#debugging) @@ -484,6 +485,19 @@ you are implementing your lint. [author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55 +## Print HIR lint + +To implement a lint, it's helpful to first understand the internal representation +that rustc uses. Clippy has the `#[clippy::dump]` attribute that prints the +[_High-Level Intermediate Representation (HIR)_] of the item, statement, or +expression that the attribute is attached to. To attach the attribute to expressions +you often need to enable `#![feature(stmt_expr_attributes)]`. + +[Here][print_hir_example] you can find an example, just select _Tools_ and run _Clippy_. + +[_High-Level Intermediate Representation (HIR)_]: https://rustc-dev-guide.rust-lang.org/hir.html +[print_hir_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=daf14db3a7f39ca467cd1b86c34b9afb + ## Documentation The final thing before submitting our PR is to add some documentation to our diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index 9af8dcc7726..816efbdaedf 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -8,6 +8,7 @@ #![allow(clippy::collapsible_else_if)] use std::ffi::OsStr; +use std::fmt::Write as _; use std::process::Command; use std::sync::atomic::{AtomicUsize, Ordering}; use std::{collections::HashMap, io::ErrorKind}; @@ -110,11 +111,12 @@ impl ClippyWarning { let lint = format!("`{}`", self.linttype); let mut output = String::from("| "); - output.push_str(&format!( + let _ = write!( + output, "[`{}`](../target/lintcheck/sources/{}#L{})", file_with_pos, file, self.line - )); - output.push_str(&format!(r#" | {:<50} | "{}" |"#, lint, self.message)); + ); + let _ = write!(output, r#" | {:<50} | "{}" |"#, lint, self.message); output.push('\n'); output } else { @@ -304,7 +306,7 @@ impl Crate { let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir"); let mut args = if fix { - vec!["--fix", "--allow-no-vcs", "--"] + vec!["--fix", "--"] } else { vec!["--", "--message-format=json", "--"] }; @@ -391,7 +393,7 @@ struct LintcheckConfig { lintcheck_results_path: PathBuf, /// whether to just run --fix and not collect all the warnings fix: bool, - /// A list of lint that this lintcheck run shound focus on + /// A list of lints that this lintcheck run should focus on lint_filter: Vec, /// Indicate if the output should support markdown syntax markdown: bool, @@ -835,10 +837,11 @@ pub fn main() { text.push_str("| file | lint | message |\n"); text.push_str("| --- | --- | --- |\n"); } - text.push_str(&format!("{}", all_msgs.join(""))); + write!(text, "{}", all_msgs.join("")); text.push_str("\n\n### ICEs:\n"); - ices.iter() - .for_each(|(cratename, msg)| text.push_str(&format!("{}: '{}'", cratename, msg))); + for (cratename, msg) in ices.iter() { + let _ = write!(text, "{}: '{}'", cratename, msg); + } println!("Writing logs to {}", config.lintcheck_results_path.display()); std::fs::create_dir_all(config.lintcheck_results_path.parent().unwrap()).unwrap(); diff --git a/rust-toolchain b/rust-toolchain index bb29c71e9f4..03acb51306d 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-04-07" +channel = "nightly-2022-05-05" components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/driver.rs b/src/driver.rs index 32a09fdb9d9..7de40fe63ac 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -165,8 +165,7 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { // Separate the output with an empty line eprintln!(); - let fallback_bundle = - rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false); + let fallback_bundle = rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false); let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr( rustc_errors::ColorConfig::Auto, None, diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 67af9d05bf4..eb97d1933d5 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -80,9 +80,13 @@ fn run_clippy_for_package(project: &str) { .args(&["-D", "clippy::pedantic"]) .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir - // internal lints only exist if we build with the internal feature if cfg!(feature = "internal") { + // internal lints only exist if we build with the internal feature command.args(&["-D", "clippy::internal"]); + } else { + // running a clippy built without internal lints on the clippy source + // that contains e.g. `allow(clippy::invalid_paths)` + command.args(&["-A", "unknown_lints"]); } let output = command.output().unwrap(); diff --git a/tests/lint_message_convention.rs b/tests/lint_message_convention.rs index dc82ba891fb..dd1d4412036 100644 --- a/tests/lint_message_convention.rs +++ b/tests/lint_message_convention.rs @@ -66,7 +66,7 @@ fn lint_message_convention() { // make sure that lint messages: // * are not capitalized - // * don't have puncuation at the end of the last sentence + // * don't have punctuation at the end of the last sentence // these directories have interesting tests let test_dirs = ["ui", "ui-cargo", "ui-internal", "ui-toml"] diff --git a/tests/ui-internal/collapsible_span_lint_calls.stderr b/tests/ui-internal/collapsible_span_lint_calls.stderr index 558d1299160..0852fe65aaf 100644 --- a/tests/ui-internal/collapsible_span_lint_calls.stderr +++ b/tests/ui-internal/collapsible_span_lint_calls.stderr @@ -29,7 +29,7 @@ LL | | db.help(help_msg); LL | | }); | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg)` -error: this call is collspible +error: this call is collapsible --> $DIR/collapsible_span_lint_calls.rs:45:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { @@ -37,7 +37,7 @@ LL | | db.span_note(expr.span, note_msg); 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 +error: this call is collapsible --> $DIR/collapsible_span_lint_calls.rs:48:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { diff --git a/tests/ui-internal/interning_defined_symbol.fixed b/tests/ui-internal/interning_defined_symbol.fixed index 6b7fd6efe39..eaea218e128 100644 --- a/tests/ui-internal/interning_defined_symbol.fixed +++ b/tests/ui-internal/interning_defined_symbol.fixed @@ -1,6 +1,6 @@ // run-rustfix #![deny(clippy::internal)] -#![allow(clippy::missing_clippy_version_attribute)] +#![allow(clippy::missing_clippy_version_attribute, clippy::let_unit_value)] #![feature(rustc_private)] extern crate rustc_span; diff --git a/tests/ui-internal/interning_defined_symbol.rs b/tests/ui-internal/interning_defined_symbol.rs index 98d7d7adad1..7efebb8fae4 100644 --- a/tests/ui-internal/interning_defined_symbol.rs +++ b/tests/ui-internal/interning_defined_symbol.rs @@ -1,6 +1,6 @@ // run-rustfix #![deny(clippy::internal)] -#![allow(clippy::missing_clippy_version_attribute)] +#![allow(clippy::missing_clippy_version_attribute, clippy::let_unit_value)] #![feature(rustc_private)] extern crate rustc_span; diff --git a/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs new file mode 100644 index 00000000000..fbef5c4564b --- /dev/null +++ b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs @@ -0,0 +1,41 @@ +#![warn(clippy::await_holding_invalid_type)] +use std::net::Ipv4Addr; + +async fn bad() -> u32 { + let _x = String::from("hello"); + baz().await +} + +async fn bad_reason() -> u32 { + let _x = Ipv4Addr::new(127, 0, 0, 1); + baz().await +} + +async fn good() -> u32 { + { + let _x = String::from("hi!"); + let _y = Ipv4Addr::new(127, 0, 0, 1); + } + baz().await; + let _x = String::from("hi!"); + 47 +} + +async fn baz() -> u32 { + 42 +} + +#[allow(clippy::manual_async_fn)] +fn block_bad() -> impl std::future::Future { + async move { + let _x = String::from("hi!"); + baz().await + } +} + +fn main() { + good(); + bad(); + bad_reason(); + block_bad(); +} diff --git a/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr new file mode 100644 index 00000000000..62c45b54634 --- /dev/null +++ b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr @@ -0,0 +1,25 @@ +error: `std::string::String` may not be held across an `await` point per `clippy.toml` + --> $DIR/await_holding_invalid_type.rs:5:9 + | +LL | let _x = String::from("hello"); + | ^^ + | + = note: `-D clippy::await-holding-invalid-type` implied by `-D warnings` + = note: strings are bad + +error: `std::net::Ipv4Addr` may not be held across an `await` point per `clippy.toml` + --> $DIR/await_holding_invalid_type.rs:10:9 + | +LL | let _x = Ipv4Addr::new(127, 0, 0, 1); + | ^^ + +error: `std::string::String` may not be held across an `await` point per `clippy.toml` + --> $DIR/await_holding_invalid_type.rs:31:13 + | +LL | let _x = String::from("hi!"); + | ^^ + | + = note: strings are bad + +error: aborting due to 3 previous errors + diff --git a/tests/ui-toml/await_holding_invalid_type/clippy.toml b/tests/ui-toml/await_holding_invalid_type/clippy.toml new file mode 100644 index 00000000000..79990096b84 --- /dev/null +++ b/tests/ui-toml/await_holding_invalid_type/clippy.toml @@ -0,0 +1,4 @@ +await-holding-invalid-types = [ + { path = "std::string::String", reason = "strings are bad" }, + "std::net::Ipv4Addr", +] diff --git a/tests/ui-toml/functions_maxlines/test.rs b/tests/ui-toml/functions_maxlines/test.rs index e678c896fd3..4ac0378544c 100644 --- a/tests/ui-toml/functions_maxlines/test.rs +++ b/tests/ui-toml/functions_maxlines/test.rs @@ -1,4 +1,5 @@ #![warn(clippy::too_many_lines)] +#![allow(clippy::let_unit_value)] // This function should be considered one line. fn many_comments_but_one_line_of_code() { diff --git a/tests/ui-toml/functions_maxlines/test.stderr b/tests/ui-toml/functions_maxlines/test.stderr index d736bf89973..dc255bdcaba 100644 --- a/tests/ui-toml/functions_maxlines/test.stderr +++ b/tests/ui-toml/functions_maxlines/test.stderr @@ -1,5 +1,5 @@ error: this function has too many lines (2/1) - --> $DIR/test.rs:18:1 + --> $DIR/test.rs:19:1 | LL | / fn too_many_lines() { LL | | println!("This is bad."); @@ -10,7 +10,7 @@ LL | | } = note: `-D clippy::too-many-lines` implied by `-D warnings` error: this function has too many lines (4/1) - --> $DIR/test.rs:24:1 + --> $DIR/test.rs:25:1 | LL | / async fn async_too_many_lines() { LL | | println!("This is bad."); @@ -19,7 +19,7 @@ LL | | } | |_^ error: this function has too many lines (4/1) - --> $DIR/test.rs:30:1 + --> $DIR/test.rs:31:1 | LL | / fn closure_too_many_lines() { LL | | let _ = { @@ -30,7 +30,7 @@ LL | | } | |_^ error: this function has too many lines (2/1) - --> $DIR/test.rs:52:1 + --> $DIR/test.rs:53:1 | LL | / fn comment_before_code() { LL | | let _ = "test"; diff --git a/tests/ui-toml/large_include_file/clippy.toml b/tests/ui-toml/large_include_file/clippy.toml new file mode 100644 index 00000000000..ea34bf9fbe0 --- /dev/null +++ b/tests/ui-toml/large_include_file/clippy.toml @@ -0,0 +1 @@ +max-include-file-size = 600 diff --git a/tests/ui-toml/large_include_file/large_include_file.rs b/tests/ui-toml/large_include_file/large_include_file.rs new file mode 100644 index 00000000000..f3dbb6ad1cf --- /dev/null +++ b/tests/ui-toml/large_include_file/large_include_file.rs @@ -0,0 +1,16 @@ +#![warn(clippy::large_include_file)] + +// Good +const GOOD_INCLUDE_BYTES: &[u8; 581] = include_bytes!("large_include_file.rs"); +const GOOD_INCLUDE_STR: &str = include_str!("large_include_file.rs"); + +#[allow(clippy::large_include_file)] +const ALLOWED_TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt"); +#[allow(clippy::large_include_file)] +const ALLOWED_TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt"); + +// Bad +const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt"); +const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt"); + +fn main() {} diff --git a/tests/ui-toml/large_include_file/large_include_file.stderr b/tests/ui-toml/large_include_file/large_include_file.stderr new file mode 100644 index 00000000000..6a685a58318 --- /dev/null +++ b/tests/ui-toml/large_include_file/large_include_file.stderr @@ -0,0 +1,21 @@ +error: attempted to include a large file + --> $DIR/large_include_file.rs:13:43 + | +LL | const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::large-include-file` implied by `-D warnings` + = note: the configuration allows a maximum size of 600 bytes + = note: this error originates in the macro `include_bytes` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: attempted to include a large file + --> $DIR/large_include_file.rs:14:35 + | +LL | const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the configuration allows a maximum size of 600 bytes + = note: this error originates in the macro `include_str` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + diff --git a/tests/ui-toml/large_include_file/too_big.txt b/tests/ui-toml/large_include_file/too_big.txt new file mode 100644 index 00000000000..9829c46bc00 --- /dev/null +++ b/tests/ui-toml/large_include_file/too_big.txt @@ -0,0 +1 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Maecenas accumsan lacus vel facilisis volutpat. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Tellus id interdum velit laoreet id donec ultrices. Est ultricies integer quis auctor elit sed vulputate. Erat velit scelerisque in dictum non consectetur a erat nam. Sed blandit libero volutpat sed. Tortor condimentum lacinia quis vel eros. Enim ut tellus elementum sagittis vitae et leo duis. Congue mauris rhoncus aenean vel elit scelerisque. Id consectetur purus ut faucibus pulvinar elementum integer. \ No newline at end of file diff --git a/tests/ui-toml/min_rust_version/min_rust_version.stderr b/tests/ui-toml/min_rust_version/min_rust_version.stderr index a1e7361c0cb..5dae5af7eb5 100644 --- a/tests/ui-toml/min_rust_version/min_rust_version.stderr +++ b/tests/ui-toml/min_rust_version/min_rust_version.stderr @@ -1,4 +1,4 @@ -error: you are using an explicit closure for copying elements +error: you are using an explicit closure for cloning elements --> $DIR/min_rust_version.rs:74:26 | LL | let _: Option = Some(&16).map(|b| *b); diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 00ddbd608a7..8701809b4da 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `max-suggested-slice-pattern-length`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `max-suggested-slice-pattern-length`, `await-holding-invalid-types`, `max-include-file-size`, `third-party` at line 5 column 1 error: aborting due to previous error diff --git a/tests/ui/assertions_on_constants.rs b/tests/ui/assertions_on_constants.rs index 7477c01ca78..7bea9563d47 100644 --- a/tests/ui/assertions_on_constants.rs +++ b/tests/ui/assertions_on_constants.rs @@ -1,4 +1,4 @@ -#![allow(non_fmt_panics)] +#![allow(non_fmt_panics, clippy::needless_bool)] macro_rules! assert_const { ($len:expr) => { @@ -28,6 +28,12 @@ fn main() { assert_const!(3); assert_const!(-1); - // Don't lint on this: + // Don't lint if based on `cfg!(..)`: assert!(cfg!(feature = "hey") || cfg!(not(feature = "asdf"))); + + let flag: bool = cfg!(not(feature = "asdf")); + assert!(flag); + + const CFG_FLAG: &bool = &cfg!(feature = "hey"); + assert!(!CFG_FLAG); } diff --git a/tests/ui/auxiliary/proc_macro_derive.rs b/tests/ui/auxiliary/proc_macro_derive.rs index 4b7b7fec78f..ed7b17651e6 100644 --- a/tests/ui/auxiliary/proc_macro_derive.rs +++ b/tests/ui/auxiliary/proc_macro_derive.rs @@ -13,7 +13,7 @@ use proc_macro::{quote, TokenStream}; #[proc_macro_derive(DeriveSomething)] pub fn derive(_: TokenStream) -> TokenStream { - // Shound not trigger `used_underscore_binding` + // Should not trigger `used_underscore_binding` let _inside_derive = 1; assert_eq!(_inside_derive, _inside_derive); diff --git a/tests/ui/auxiliary/proc_macro_with_span.rs b/tests/ui/auxiliary/proc_macro_with_span.rs new file mode 100644 index 00000000000..8ea631f2bbd --- /dev/null +++ b/tests/ui/auxiliary/proc_macro_with_span.rs @@ -0,0 +1,32 @@ +// compile-flags: --emit=link +// no-prefer-dynamic + +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::{token_stream::IntoIter, Group, Span, TokenStream, TokenTree}; + +#[proc_macro] +pub fn with_span(input: TokenStream) -> TokenStream { + let mut iter = input.into_iter(); + let span = iter.next().unwrap().span(); + let mut res = TokenStream::new(); + write_with_span(span, iter, &mut res); + res +} + +fn write_with_span(s: Span, input: IntoIter, out: &mut TokenStream) { + for mut tt in input { + if let TokenTree::Group(g) = tt { + let mut stream = TokenStream::new(); + write_with_span(s, g.stream().into_iter(), &mut stream); + let mut group = Group::new(g.delimiter(), stream); + group.set_span(s); + out.extend([TokenTree::Group(group)]); + } else { + tt.set_span(s); + out.extend([tt]); + } + } +} diff --git a/tests/ui/bytes_count_to_len.fixed b/tests/ui/bytes_count_to_len.fixed new file mode 100644 index 00000000000..860642363b5 --- /dev/null +++ b/tests/ui/bytes_count_to_len.fixed @@ -0,0 +1,34 @@ +// run-rustfix +#![warn(clippy::bytes_count_to_len)] +use std::fs::File; +use std::io::Read; + +fn main() { + // should fix, because type is String + let _ = String::from("foo").len(); + + let s1 = String::from("foo"); + let _ = s1.len(); + + // should fix, because type is &str + let _ = "foo".len(); + + let s2 = "foo"; + let _ = s2.len(); + + // make sure using count() normally doesn't trigger warning + let vector = [0, 1, 2]; + let _ = vector.iter().count(); + + // The type is slice, so should not fix + let _ = &[1, 2, 3].bytes().count(); + + let bytes: &[u8] = &[1, 2, 3]; + bytes.bytes().count(); + + // The type is File, so should not fix + let _ = File::open("foobar").unwrap().bytes().count(); + + let f = File::open("foobar").unwrap(); + let _ = f.bytes().count(); +} diff --git a/tests/ui/bytes_count_to_len.rs b/tests/ui/bytes_count_to_len.rs new file mode 100644 index 00000000000..162730c2842 --- /dev/null +++ b/tests/ui/bytes_count_to_len.rs @@ -0,0 +1,34 @@ +// run-rustfix +#![warn(clippy::bytes_count_to_len)] +use std::fs::File; +use std::io::Read; + +fn main() { + // should fix, because type is String + let _ = String::from("foo").bytes().count(); + + let s1 = String::from("foo"); + let _ = s1.bytes().count(); + + // should fix, because type is &str + let _ = "foo".bytes().count(); + + let s2 = "foo"; + let _ = s2.bytes().count(); + + // make sure using count() normally doesn't trigger warning + let vector = [0, 1, 2]; + let _ = vector.iter().count(); + + // The type is slice, so should not fix + let _ = &[1, 2, 3].bytes().count(); + + let bytes: &[u8] = &[1, 2, 3]; + bytes.bytes().count(); + + // The type is File, so should not fix + let _ = File::open("foobar").unwrap().bytes().count(); + + let f = File::open("foobar").unwrap(); + let _ = f.bytes().count(); +} diff --git a/tests/ui/bytes_count_to_len.stderr b/tests/ui/bytes_count_to_len.stderr new file mode 100644 index 00000000000..224deb77987 --- /dev/null +++ b/tests/ui/bytes_count_to_len.stderr @@ -0,0 +1,28 @@ +error: using long and hard to read `.bytes().count()` + --> $DIR/bytes_count_to_len.rs:8:13 + | +LL | let _ = String::from("foo").bytes().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `String::from("foo").len()` + | + = note: `-D clippy::bytes-count-to-len` implied by `-D warnings` + +error: using long and hard to read `.bytes().count()` + --> $DIR/bytes_count_to_len.rs:11:13 + | +LL | let _ = s1.bytes().count(); + | ^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `s1.len()` + +error: using long and hard to read `.bytes().count()` + --> $DIR/bytes_count_to_len.rs:14:13 + | +LL | let _ = "foo".bytes().count(); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `"foo".len()` + +error: using long and hard to read `.bytes().count()` + --> $DIR/bytes_count_to_len.rs:17:13 + | +LL | let _ = s2.bytes().count(); + | ^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `s2.len()` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/cast.rs b/tests/ui/cast.rs index cf85a5ca931..e6031e9adae 100644 --- a/tests/ui/cast.rs +++ b/tests/ui/cast.rs @@ -1,13 +1,13 @@ #![feature(repr128)] #![allow(incomplete_features)] - -#[warn( +#![warn( clippy::cast_precision_loss, clippy::cast_possible_truncation, clippy::cast_sign_loss, clippy::cast_possible_wrap )] -#[allow(clippy::cast_abs_to_unsigned, clippy::no_effect, clippy::unnecessary_operation)] +#![allow(clippy::cast_abs_to_unsigned, clippy::no_effect, clippy::unnecessary_operation)] + fn main() { // Test clippy::cast_precision_loss let x0 = 1i32; @@ -252,3 +252,11 @@ fn main() { } } } + +fn avoid_subtract_overflow(q: u32) { + let c = (q >> 16) as u8; + c as usize; + + let c = (q / 1000) as u8; + c as usize; +} diff --git a/tests/ui/cast.stderr b/tests/ui/cast.stderr index 7a68c0984f1..0c63b4af308 100644 --- a/tests/ui/cast.stderr +++ b/tests/ui/cast.stderr @@ -194,5 +194,17 @@ error: casting `main::E10` to `u16` may truncate the value LL | let _ = self as u16; | ^^^^^^^^^^^ -error: aborting due to 31 previous errors +error: casting `u32` to `u8` may truncate the value + --> $DIR/cast.rs:257:13 + | +LL | let c = (q >> 16) as u8; + | ^^^^^^^^^^^^^^^ + +error: casting `u32` to `u8` may truncate the value + --> $DIR/cast.rs:260:13 + | +LL | let c = (q / 1000) as u8; + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 33 previous errors diff --git a/tests/ui/cast_alignment.rs b/tests/ui/cast_alignment.rs index e4e7290a30e..95bb883df1b 100644 --- a/tests/ui/cast_alignment.rs +++ b/tests/ui/cast_alignment.rs @@ -44,8 +44,8 @@ fn main() { let _ = core::ptr::read_unaligned(ptr as *const u16); let _ = core::intrinsics::unaligned_volatile_load(ptr as *const u16); let ptr = &mut data as *mut [u8; 2] as *mut u8; - let _ = (ptr as *mut u16).write_unaligned(0); - let _ = core::ptr::write_unaligned(ptr as *mut u16, 0); - let _ = core::intrinsics::unaligned_volatile_store(ptr as *mut u16, 0); + (ptr as *mut u16).write_unaligned(0); + core::ptr::write_unaligned(ptr as *mut u16, 0); + core::intrinsics::unaligned_volatile_store(ptr as *mut u16, 0); } } diff --git a/tests/ui/cast_slice_different_sizes.rs b/tests/ui/cast_slice_different_sizes.rs index cfe1cca2eba..24d7eb28a19 100644 --- a/tests/ui/cast_slice_different_sizes.rs +++ b/tests/ui/cast_slice_different_sizes.rs @@ -1,3 +1,5 @@ +#![allow(clippy::let_unit_value)] + fn main() { let x: [i32; 3] = [1_i32, 2, 3]; let r_x = &x; @@ -37,3 +39,44 @@ fn main() { let long_chain_restore = r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const [u8] as *const [u32]; } + +// foo and foo2 should not fire, they're the same size +fn foo(x: *mut [u8]) -> *mut [u8] { + x as *mut [u8] +} + +fn foo2(x: *mut [u8]) -> *mut [u8] { + x as *mut _ +} + +// Test that casts as part of function returns work +fn bar(x: *mut [u16]) -> *mut [u8] { + x as *mut [u8] +} + +fn uwu(x: *mut [u16]) -> *mut [u8] { + x as *mut _ +} + +fn bar2(x: *mut [u16]) -> *mut [u8] { + x as _ +} + +// constify +fn bar3(x: *mut [u16]) -> *const [u8] { + x as _ +} + +// unconstify +fn bar4(x: *const [u16]) -> *mut [u8] { + x as _ +} + +// function returns plus blocks +fn blocks(x: *mut [u16]) -> *mut [u8] { + ({ x }) as _ +} + +fn more_blocks(x: *mut [u16]) -> *mut [u8] { + { ({ x }) as _ } +} diff --git a/tests/ui/cast_slice_different_sizes.stderr b/tests/ui/cast_slice_different_sizes.stderr index a37cec7cb3b..40721dcd05d 100644 --- a/tests/ui/cast_slice_different_sizes.stderr +++ b/tests/ui/cast_slice_different_sizes.stderr @@ -1,5 +1,5 @@ error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count - --> $DIR/cast_slice_different_sizes.rs:7:13 + --> $DIR/cast_slice_different_sizes.rs:9:13 | LL | let b = a as *const [u8]; | ^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(a as *const u8, ..)` @@ -7,25 +7,25 @@ LL | let b = a as *const [u8]; = note: `#[deny(clippy::cast_slice_different_sizes)]` on by default error: casting between raw pointers to `[u8]` (element size 1) and `[u32]` (element size 4) does not adjust the count - --> $DIR/cast_slice_different_sizes.rs:8:13 + --> $DIR/cast_slice_different_sizes.rs:10:13 | LL | let c = b as *const [u32]; | ^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(b as *const u32, ..)` error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count - --> $DIR/cast_slice_different_sizes.rs:11:16 + --> $DIR/cast_slice_different_sizes.rs:13:16 | LL | let loss = r_x as *const [i32] as *const [u8]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(r_x as *const [i32] as *const u8, ..)` error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count - --> $DIR/cast_slice_different_sizes.rs:18:24 + --> $DIR/cast_slice_different_sizes.rs:20:24 | LL | let loss_block_1 = { r_x as *const [i32] } as *const [u8]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts({ r_x as *const [i32] } as *const u8, ..)` error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count - --> $DIR/cast_slice_different_sizes.rs:19:24 + --> $DIR/cast_slice_different_sizes.rs:21:24 | LL | let loss_block_2 = { | ________________________^ @@ -43,10 +43,79 @@ LL ~ } as *const u8, ..); | error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count - --> $DIR/cast_slice_different_sizes.rs:36:27 + --> $DIR/cast_slice_different_sizes.rs:38:27 | LL | let long_chain_loss = r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const [u8]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const u8, ..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(r_x as *const [i32] as *const u8, ..)` -error: aborting due to 6 previous errors +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:53:36 + | +LL | fn bar(x: *mut [u16]) -> *mut [u8] { + | ____________________________________^ +LL | | x as *mut [u8] +LL | | } + | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)` + +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:57:36 + | +LL | fn uwu(x: *mut [u16]) -> *mut [u8] { + | ____________________________________^ +LL | | x as *mut _ +LL | | } + | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)` + +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:61:37 + | +LL | fn bar2(x: *mut [u16]) -> *mut [u8] { + | _____________________________________^ +LL | | x as _ +LL | | } + | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)` + +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:66:39 + | +LL | fn bar3(x: *mut [u16]) -> *const [u8] { + | _______________________________________^ +LL | | x as _ +LL | | } + | |_^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(x as *const u8, ..)` + +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:71:39 + | +LL | fn bar4(x: *const [u16]) -> *mut [u8] { + | _______________________________________^ +LL | | x as _ +LL | | } + | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)` + +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:76:39 + | +LL | fn blocks(x: *mut [u16]) -> *mut [u8] { + | _______________________________________^ +LL | | ({ x }) as _ +LL | | } + | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(({ x }) as *mut u8, ..)` + +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:80:44 + | +LL | fn more_blocks(x: *mut [u16]) -> *mut [u8] { + | ____________________________________________^ +LL | | { ({ x }) as _ } +LL | | } + | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(({ x }) as *mut u8, ..)` + +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:81:5 + | +LL | { ({ x }) as _ } + | ^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(({ x }) as *mut u8, ..)` + +error: aborting due to 14 previous errors diff --git a/tests/ui/collapsible_else_if.fixed b/tests/ui/collapsible_else_if.fixed index bb6c4c0703d..d6a5a785067 100644 --- a/tests/ui/collapsible_else_if.fixed +++ b/tests/ui/collapsible_else_if.fixed @@ -75,3 +75,10 @@ fn main() { } } } + +#[rustfmt::skip] +#[allow(dead_code)] +fn issue_7318() { + if true { println!("I've been resolved!") + }else if false {} +} diff --git a/tests/ui/collapsible_else_if.rs b/tests/ui/collapsible_else_if.rs index 6d4f688db8c..4399fc8b2bd 100644 --- a/tests/ui/collapsible_else_if.rs +++ b/tests/ui/collapsible_else_if.rs @@ -89,3 +89,12 @@ fn main() { } } } + +#[rustfmt::skip] +#[allow(dead_code)] +fn issue_7318() { + if true { println!("I've been resolved!") + }else{ + if false {} + } +} diff --git a/tests/ui/collapsible_else_if.stderr b/tests/ui/collapsible_else_if.stderr index 6970f660979..45b2094c994 100644 --- a/tests/ui/collapsible_else_if.stderr +++ b/tests/ui/collapsible_else_if.stderr @@ -150,5 +150,14 @@ LL + println!("!") LL + } | -error: aborting due to 7 previous errors +error: this `else { if .. }` block can be collapsed + --> $DIR/collapsible_else_if.rs:97:10 + | +LL | }else{ + | __________^ +LL | | if false {} +LL | | } + | |_____^ help: collapse nested if block: `if false {}` + +error: aborting due to 8 previous errors diff --git a/tests/ui/crashes/auxiliary/ice-8681-aux.rs b/tests/ui/crashes/auxiliary/ice-8681-aux.rs new file mode 100644 index 00000000000..95b63151325 --- /dev/null +++ b/tests/ui/crashes/auxiliary/ice-8681-aux.rs @@ -0,0 +1,6 @@ +pub fn foo(x: &u32) -> u32 { + /* Safety: + * This is totally ok. + */ + unsafe { *(x as *const u32) } +} diff --git a/tests/ui/crashes/ice-2865.rs b/tests/ui/crashes/ice-2865.rs index 6b1ceb50569..c6298139601 100644 --- a/tests/ui/crashes/ice-2865.rs +++ b/tests/ui/crashes/ice-2865.rs @@ -1,4 +1,4 @@ -#[allow(dead_code)] +#![allow(dead_code, clippy::extra_unused_lifetimes)] /// Test for https://github.com/rust-lang/rust-clippy/issues/2865 diff --git a/tests/ui/crashes/ice-3151.rs b/tests/ui/crashes/ice-3151.rs index fef4d7db84d..268ba86fc7a 100644 --- a/tests/ui/crashes/ice-3151.rs +++ b/tests/ui/crashes/ice-3151.rs @@ -1,4 +1,4 @@ -/// Test for https://github.com/rust-lang/rust-clippy/issues/2865 +/// Test for https://github.com/rust-lang/rust-clippy/issues/3151 #[derive(Clone)] pub struct HashMap { diff --git a/tests/ui/crashes/ice-5944.rs b/tests/ui/crashes/ice-5944.rs index 5caf29c6197..ce46bc1acc1 100644 --- a/tests/ui/crashes/ice-5944.rs +++ b/tests/ui/crashes/ice-5944.rs @@ -1,4 +1,5 @@ #![warn(clippy::repeat_once)] +#![allow(clippy::let_unit_value)] trait Repeat { fn repeat(&self) {} diff --git a/tests/ui/crashes/ice-8250.stderr b/tests/ui/crashes/ice-8250.stderr index 04ea4456656..8ed8f3b3a06 100644 --- a/tests/ui/crashes/ice-8250.stderr +++ b/tests/ui/crashes/ice-8250.stderr @@ -1,11 +1,3 @@ -error: manual implementation of `split_once` - --> $DIR/ice-8250.rs:2:13 - | -LL | let _ = s[1..].splitn(2, '.').next()?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s[1..].split_once('.').map_or(s[1..], |x| x.0)` - | - = note: `-D clippy::manual-split-once` implied by `-D warnings` - error: unnecessary use of `splitn` --> $DIR/ice-8250.rs:2:13 | @@ -14,5 +6,5 @@ LL | let _ = s[1..].splitn(2, '.').next()?; | = note: `-D clippy::needless-splitn` implied by `-D warnings` -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/tests/ui/crashes/ice-8681.rs b/tests/ui/crashes/ice-8681.rs new file mode 100644 index 00000000000..ee14f011f63 --- /dev/null +++ b/tests/ui/crashes/ice-8681.rs @@ -0,0 +1,10 @@ +// aux-build: ice-8681-aux.rs + +#![warn(clippy::undocumented_unsafe_blocks)] + +#[path = "auxiliary/ice-8681-aux.rs"] +mod ice_8681_aux; + +fn main() { + let _ = ice_8681_aux::foo(&0u32); +} diff --git a/tests/ui/crashes/ice-96721.rs b/tests/ui/crashes/ice-96721.rs new file mode 100644 index 00000000000..4b3fb764010 --- /dev/null +++ b/tests/ui/crashes/ice-96721.rs @@ -0,0 +1,10 @@ +macro_rules! foo { + () => { + "bar.rs" + }; +} + +#[path = foo!()] //~ ERROR malformed `path` attribute +mod abc {} + +fn main() {} diff --git a/tests/ui/crashes/ice-96721.stderr b/tests/ui/crashes/ice-96721.stderr new file mode 100644 index 00000000000..78c567b8e77 --- /dev/null +++ b/tests/ui/crashes/ice-96721.stderr @@ -0,0 +1,8 @@ +error: malformed `path` attribute input + --> $DIR/ice-96721.rs:7:1 + | +LL | #[path = foo!()] //~ ERROR malformed `path` attribute + | ^^^^^^^^^^^^^^^^ help: must be of the form: `#[path = "file"]` + +error: aborting due to previous error + diff --git a/tests/ui/default_numeric_fallback_f64.fixed b/tests/ui/default_numeric_fallback_f64.fixed index e0b4a2f6942..a28bff76755 100644 --- a/tests/ui/default_numeric_fallback_f64.fixed +++ b/tests/ui/default_numeric_fallback_f64.fixed @@ -2,12 +2,15 @@ // aux-build:macro_rules.rs #![warn(clippy::default_numeric_fallback)] -#![allow(unused)] -#![allow(clippy::never_loop)] -#![allow(clippy::no_effect)] -#![allow(clippy::unnecessary_operation)] -#![allow(clippy::branches_sharing_code)] -#![allow(clippy::match_single_binding)] +#![allow( + unused, + clippy::never_loop, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::branches_sharing_code, + clippy::match_single_binding, + clippy::let_unit_value +)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/default_numeric_fallback_f64.rs b/tests/ui/default_numeric_fallback_f64.rs index 50bbb6eec6c..b48435cc7b2 100644 --- a/tests/ui/default_numeric_fallback_f64.rs +++ b/tests/ui/default_numeric_fallback_f64.rs @@ -2,12 +2,15 @@ // aux-build:macro_rules.rs #![warn(clippy::default_numeric_fallback)] -#![allow(unused)] -#![allow(clippy::never_loop)] -#![allow(clippy::no_effect)] -#![allow(clippy::unnecessary_operation)] -#![allow(clippy::branches_sharing_code)] -#![allow(clippy::match_single_binding)] +#![allow( + unused, + clippy::never_loop, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::branches_sharing_code, + clippy::match_single_binding, + clippy::let_unit_value +)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/default_numeric_fallback_f64.stderr b/tests/ui/default_numeric_fallback_f64.stderr index f8a2407b693..f8b6c7746ed 100644 --- a/tests/ui/default_numeric_fallback_f64.stderr +++ b/tests/ui/default_numeric_fallback_f64.stderr @@ -1,5 +1,5 @@ error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:18:17 + --> $DIR/default_numeric_fallback_f64.rs:21:17 | LL | let x = 0.12; | ^^^^ help: consider adding suffix: `0.12_f64` @@ -7,133 +7,133 @@ LL | let x = 0.12; = note: `-D clippy::default-numeric-fallback` implied by `-D warnings` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:19:18 + --> $DIR/default_numeric_fallback_f64.rs:22:18 | LL | let x = [1., 2., 3.]; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:19:22 + --> $DIR/default_numeric_fallback_f64.rs:22:22 | LL | let x = [1., 2., 3.]; | ^^ help: consider adding suffix: `2.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:19:26 + --> $DIR/default_numeric_fallback_f64.rs:22:26 | LL | let x = [1., 2., 3.]; | ^^ help: consider adding suffix: `3.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:20:28 + --> $DIR/default_numeric_fallback_f64.rs:23:28 | LL | let x = if true { (1., 2.) } else { (3., 4.) }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:20:32 + --> $DIR/default_numeric_fallback_f64.rs:23:32 | LL | let x = if true { (1., 2.) } else { (3., 4.) }; | ^^ help: consider adding suffix: `2.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:20:46 + --> $DIR/default_numeric_fallback_f64.rs:23:46 | LL | let x = if true { (1., 2.) } else { (3., 4.) }; | ^^ help: consider adding suffix: `3.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:20:50 + --> $DIR/default_numeric_fallback_f64.rs:23:50 | LL | let x = if true { (1., 2.) } else { (3., 4.) }; | ^^ help: consider adding suffix: `4.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:21:23 + --> $DIR/default_numeric_fallback_f64.rs:24:23 | LL | let x = match 1. { | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:22:18 + --> $DIR/default_numeric_fallback_f64.rs:25:18 | LL | _ => 1., | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:40:21 + --> $DIR/default_numeric_fallback_f64.rs:43:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:48:21 + --> $DIR/default_numeric_fallback_f64.rs:51:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:54:21 + --> $DIR/default_numeric_fallback_f64.rs:57:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:66:9 + --> $DIR/default_numeric_fallback_f64.rs:69:9 | LL | 1. | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:72:27 + --> $DIR/default_numeric_fallback_f64.rs:75:27 | LL | let f = || -> _ { 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:76:29 + --> $DIR/default_numeric_fallback_f64.rs:79:29 | LL | let f = || -> f64 { 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:90:21 + --> $DIR/default_numeric_fallback_f64.rs:93:21 | LL | generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:93:32 + --> $DIR/default_numeric_fallback_f64.rs:96:32 | LL | let x: _ = generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:111:28 + --> $DIR/default_numeric_fallback_f64.rs:114:28 | LL | GenericStruct { x: 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:114:36 + --> $DIR/default_numeric_fallback_f64.rs:117:36 | LL | let _ = GenericStruct { x: 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:132:24 + --> $DIR/default_numeric_fallback_f64.rs:135:24 | LL | GenericEnum::X(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:152:23 + --> $DIR/default_numeric_fallback_f64.rs:155:23 | LL | s.generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:159:21 + --> $DIR/default_numeric_fallback_f64.rs:162:21 | LL | let x = 22.; | ^^^ help: consider adding suffix: `22.0_f64` diff --git a/tests/ui/default_numeric_fallback_i32.fixed b/tests/ui/default_numeric_fallback_i32.fixed index bded9e2c0e8..fa85d278c8f 100644 --- a/tests/ui/default_numeric_fallback_i32.fixed +++ b/tests/ui/default_numeric_fallback_i32.fixed @@ -2,11 +2,14 @@ // aux-build:macro_rules.rs #![warn(clippy::default_numeric_fallback)] -#![allow(unused)] -#![allow(clippy::never_loop)] -#![allow(clippy::no_effect)] -#![allow(clippy::unnecessary_operation)] -#![allow(clippy::branches_sharing_code)] +#![allow( + unused, + clippy::never_loop, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::branches_sharing_code, + clippy::let_unit_value +)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/default_numeric_fallback_i32.rs b/tests/ui/default_numeric_fallback_i32.rs index 3fceefa551c..71acccd702b 100644 --- a/tests/ui/default_numeric_fallback_i32.rs +++ b/tests/ui/default_numeric_fallback_i32.rs @@ -2,11 +2,14 @@ // aux-build:macro_rules.rs #![warn(clippy::default_numeric_fallback)] -#![allow(unused)] -#![allow(clippy::never_loop)] -#![allow(clippy::no_effect)] -#![allow(clippy::unnecessary_operation)] -#![allow(clippy::branches_sharing_code)] +#![allow( + unused, + clippy::never_loop, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::branches_sharing_code, + clippy::let_unit_value +)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/default_numeric_fallback_i32.stderr b/tests/ui/default_numeric_fallback_i32.stderr index 6f9e124704b..3cc84ff1132 100644 --- a/tests/ui/default_numeric_fallback_i32.stderr +++ b/tests/ui/default_numeric_fallback_i32.stderr @@ -1,5 +1,5 @@ error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:17:17 + --> $DIR/default_numeric_fallback_i32.rs:20:17 | LL | let x = 22; | ^^ help: consider adding suffix: `22_i32` @@ -7,145 +7,145 @@ LL | let x = 22; = note: `-D clippy::default-numeric-fallback` implied by `-D warnings` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:18:18 + --> $DIR/default_numeric_fallback_i32.rs:21:18 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:18:21 + --> $DIR/default_numeric_fallback_i32.rs:21:21 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:18:24 + --> $DIR/default_numeric_fallback_i32.rs:21:24 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `3_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:19:28 + --> $DIR/default_numeric_fallback_i32.rs:22:28 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:19:31 + --> $DIR/default_numeric_fallback_i32.rs:22:31 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:19:44 + --> $DIR/default_numeric_fallback_i32.rs:22:44 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `3_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:19:47 + --> $DIR/default_numeric_fallback_i32.rs:22:47 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `4_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:20:23 + --> $DIR/default_numeric_fallback_i32.rs:23:23 | LL | let x = match 1 { | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:21:13 + --> $DIR/default_numeric_fallback_i32.rs:24:13 | LL | 1 => 1, | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:21:18 + --> $DIR/default_numeric_fallback_i32.rs:24:18 | LL | 1 => 1, | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:22:18 + --> $DIR/default_numeric_fallback_i32.rs:25:18 | LL | _ => 2, | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:39:21 + --> $DIR/default_numeric_fallback_i32.rs:42:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:47:21 + --> $DIR/default_numeric_fallback_i32.rs:50:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:53:21 + --> $DIR/default_numeric_fallback_i32.rs:56:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:65:9 + --> $DIR/default_numeric_fallback_i32.rs:68:9 | LL | 1 | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:71:27 + --> $DIR/default_numeric_fallback_i32.rs:74:27 | LL | let f = || -> _ { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:75:29 + --> $DIR/default_numeric_fallback_i32.rs:78:29 | LL | let f = || -> i32 { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:89:21 + --> $DIR/default_numeric_fallback_i32.rs:92:21 | LL | generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:92:32 + --> $DIR/default_numeric_fallback_i32.rs:95:32 | LL | let x: _ = generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:110:28 + --> $DIR/default_numeric_fallback_i32.rs:113:28 | LL | GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:113:36 + --> $DIR/default_numeric_fallback_i32.rs:116:36 | LL | let _ = GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:131:24 + --> $DIR/default_numeric_fallback_i32.rs:134:24 | LL | GenericEnum::X(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:151:23 + --> $DIR/default_numeric_fallback_i32.rs:154:23 | LL | s.generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:158:21 + --> $DIR/default_numeric_fallback_i32.rs:161:21 | LL | let x = 22; | ^^ help: consider adding suffix: `22_i32` diff --git a/tests/ui/deprecated.rs b/tests/ui/deprecated.rs index 39a2601fee9..07270bd7636 100644 --- a/tests/ui/deprecated.rs +++ b/tests/ui/deprecated.rs @@ -1,3 +1,7 @@ +// This file was generated by `cargo dev update_lints`. +// Use that command to update this file and do not edit by hand. +// Manual edits will be overwritten. + #![warn(clippy::should_assert_eq)] #![warn(clippy::extend_from_slice)] #![warn(clippy::range_step_by_zero)] diff --git a/tests/ui/deprecated.stderr b/tests/ui/deprecated.stderr index 6095f134d55..0e142ac8f20 100644 --- a/tests/ui/deprecated.stderr +++ b/tests/ui/deprecated.stderr @@ -1,5 +1,5 @@ error: lint `clippy::should_assert_eq` has been removed: `assert!()` will be more flexible with RFC 2011 - --> $DIR/deprecated.rs:1:9 + --> $DIR/deprecated.rs:5:9 | LL | #![warn(clippy::should_assert_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,91 +7,91 @@ LL | #![warn(clippy::should_assert_eq)] = note: `-D renamed-and-removed-lints` implied by `-D warnings` error: lint `clippy::extend_from_slice` has been removed: `.extend_from_slice(_)` is a faster way to extend a Vec by a slice - --> $DIR/deprecated.rs:2:9 + --> $DIR/deprecated.rs:6:9 | LL | #![warn(clippy::extend_from_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::range_step_by_zero` has been removed: `iterator.step_by(0)` panics nowadays - --> $DIR/deprecated.rs:3:9 + --> $DIR/deprecated.rs:7:9 | LL | #![warn(clippy::range_step_by_zero)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` has been stabilized in 1.7 - --> $DIR/deprecated.rs:4:9 + --> $DIR/deprecated.rs:8:9 | LL | #![warn(clippy::unstable_as_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` has been stabilized in 1.7 - --> $DIR/deprecated.rs:5:9 + --> $DIR/deprecated.rs:9:9 | LL | #![warn(clippy::unstable_as_mut_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::misaligned_transmute` has been removed: this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr - --> $DIR/deprecated.rs:6:9 + --> $DIR/deprecated.rs:10:9 | LL | #![warn(clippy::misaligned_transmute)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::assign_ops` has been removed: using compound assignment operators (e.g., `+=`) is harmless - --> $DIR/deprecated.rs:7:9 + --> $DIR/deprecated.rs:11:9 | LL | #![warn(clippy::assign_ops)] | ^^^^^^^^^^^^^^^^^^ error: lint `clippy::if_let_redundant_pattern_matching` has been removed: this lint has been changed to redundant_pattern_matching - --> $DIR/deprecated.rs:8:9 + --> $DIR/deprecated.rs:12:9 | LL | #![warn(clippy::if_let_redundant_pattern_matching)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::unsafe_vector_initialization` has been removed: the replacement suggested by this lint had substantially different behavior - --> $DIR/deprecated.rs:9:9 + --> $DIR/deprecated.rs:13:9 | LL | #![warn(clippy::unsafe_vector_initialization)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::unused_collect` has been removed: `collect` has been marked as #[must_use] in rustc and that covers all cases of this lint - --> $DIR/deprecated.rs:10:9 + --> $DIR/deprecated.rs:14:9 | LL | #![warn(clippy::unused_collect)] | ^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::replace_consts` has been removed: associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants - --> $DIR/deprecated.rs:11:9 + --> $DIR/deprecated.rs:15:9 | LL | #![warn(clippy::replace_consts)] | ^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::regex_macro` has been removed: the regex! macro has been removed from the regex crate in 2018 - --> $DIR/deprecated.rs:12:9 + --> $DIR/deprecated.rs:16:9 | LL | #![warn(clippy::regex_macro)] | ^^^^^^^^^^^^^^^^^^^ error: lint `clippy::find_map` has been removed: this lint has been replaced by `manual_find_map`, a more specific lint - --> $DIR/deprecated.rs:13:9 + --> $DIR/deprecated.rs:17:9 | 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:9 + --> $DIR/deprecated.rs:18:9 | LL | #![warn(clippy::filter_map)] | ^^^^^^^^^^^^^^^^^^ error: lint `clippy::pub_enum_variant_names` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items - --> $DIR/deprecated.rs:15:9 + --> $DIR/deprecated.rs:19:9 | LL | #![warn(clippy::pub_enum_variant_names)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::wrong_pub_self_convention` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items - --> $DIR/deprecated.rs:16:9 + --> $DIR/deprecated.rs:20:9 | LL | #![warn(clippy::wrong_pub_self_convention)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/doc_unsafe.rs b/tests/ui/doc_unsafe.rs index 4464a21b3b6..b91f7aa0dd8 100644 --- a/tests/ui/doc_unsafe.rs +++ b/tests/ui/doc_unsafe.rs @@ -1,5 +1,7 @@ // aux-build:doc_unsafe_macros.rs +#![allow(clippy::let_unit_value)] + #[macro_use] extern crate doc_unsafe_macros; diff --git a/tests/ui/doc_unsafe.stderr b/tests/ui/doc_unsafe.stderr index d68b8a0c67b..904b88eaef6 100644 --- a/tests/ui/doc_unsafe.stderr +++ b/tests/ui/doc_unsafe.stderr @@ -1,5 +1,5 @@ error: unsafe function's docs miss `# Safety` section - --> $DIR/doc_unsafe.rs:7:1 + --> $DIR/doc_unsafe.rs:9:1 | LL | / pub unsafe fn destroy_the_planet() { LL | | unimplemented!(); @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::missing-safety-doc` implied by `-D warnings` error: unsafe function's docs miss `# Safety` section - --> $DIR/doc_unsafe.rs:30:5 + --> $DIR/doc_unsafe.rs:32:5 | LL | / pub unsafe fn republished() { LL | | unimplemented!(); @@ -17,13 +17,13 @@ LL | | } | |_____^ error: unsafe function's docs miss `# Safety` section - --> $DIR/doc_unsafe.rs:38:5 + --> $DIR/doc_unsafe.rs:40:5 | LL | unsafe fn woefully_underdocumented(self); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for unsafe trait missing `# Safety` section - --> $DIR/doc_unsafe.rs:44:1 + --> $DIR/doc_unsafe.rs:46:1 | LL | / pub unsafe trait UnsafeTrait { LL | | fn method(); @@ -31,7 +31,7 @@ LL | | } | |_^ error: unsafe function's docs miss `# Safety` section - --> $DIR/doc_unsafe.rs:74:5 + --> $DIR/doc_unsafe.rs:76:5 | LL | / pub unsafe fn more_undocumented_unsafe() -> Self { LL | | unimplemented!(); @@ -39,7 +39,7 @@ LL | | } | |_____^ error: unsafe function's docs miss `# Safety` section - --> $DIR/doc_unsafe.rs:90:9 + --> $DIR/doc_unsafe.rs:92:9 | LL | / pub unsafe fn whee() { LL | | unimplemented!() diff --git a/tests/ui/drop_non_drop.stderr b/tests/ui/drop_non_drop.stderr index f73068901c5..30121033de7 100644 --- a/tests/ui/drop_non_drop.stderr +++ b/tests/ui/drop_non_drop.stderr @@ -1,4 +1,4 @@ -error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends it's contained lifetimes +error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends its contained lifetimes --> $DIR/drop_non_drop.rs:22:5 | LL | drop(Foo); @@ -11,7 +11,7 @@ note: argument has type `main::Foo` LL | drop(Foo); | ^^^ -error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends it's contained lifetimes +error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends its contained lifetimes --> $DIR/drop_non_drop.rs:37:5 | LL | drop(Baz(Foo)); diff --git a/tests/ui/empty_drop.fixed b/tests/ui/empty_drop.fixed new file mode 100644 index 00000000000..2e1b768461a --- /dev/null +++ b/tests/ui/empty_drop.fixed @@ -0,0 +1,24 @@ +// run-rustfix +#![warn(clippy::empty_drop)] +#![allow(unused)] + +// should cause an error +struct Foo; + + + +// shouldn't cause an error +struct Bar; + +impl Drop for Bar { + fn drop(&mut self) { + println!("dropping bar!"); + } +} + +// should error +struct Baz; + + + +fn main() {} diff --git a/tests/ui/empty_drop.rs b/tests/ui/empty_drop.rs new file mode 100644 index 00000000000..75232b0334d --- /dev/null +++ b/tests/ui/empty_drop.rs @@ -0,0 +1,30 @@ +// run-rustfix +#![warn(clippy::empty_drop)] +#![allow(unused)] + +// should cause an error +struct Foo; + +impl Drop for Foo { + fn drop(&mut self) {} +} + +// shouldn't cause an error +struct Bar; + +impl Drop for Bar { + fn drop(&mut self) { + println!("dropping bar!"); + } +} + +// should error +struct Baz; + +impl Drop for Baz { + fn drop(&mut self) { + {} + } +} + +fn main() {} diff --git a/tests/ui/empty_drop.stderr b/tests/ui/empty_drop.stderr new file mode 100644 index 00000000000..70f7880d036 --- /dev/null +++ b/tests/ui/empty_drop.stderr @@ -0,0 +1,22 @@ +error: empty drop implementation + --> $DIR/empty_drop.rs:8:1 + | +LL | / impl Drop for Foo { +LL | | fn drop(&mut self) {} +LL | | } + | |_^ help: try removing this impl + | + = note: `-D clippy::empty-drop` implied by `-D warnings` + +error: empty drop implementation + --> $DIR/empty_drop.rs:24:1 + | +LL | / impl Drop for Baz { +LL | | fn drop(&mut self) { +LL | | {} +LL | | } +LL | | } + | |_^ help: try removing this impl + +error: aborting due to 2 previous errors + diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index 5aedbea381f..6c2272f4dff 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -37,7 +37,7 @@ fn main() { } // See #815 - let e = Some(1u8).map(divergent); + let e = Some(1u8).map(|a| divergent(a)); let e = Some(1u8).map(generic); let e = Some(1u8).map(generic); // See #515 @@ -211,6 +211,10 @@ fn mutable_closure_in_loop() { let mut closure = |n| value += n; for _ in 0..5 { Some(1).map(&mut closure); + + let mut value = 0; + let mut in_loop = |n| value += n; + Some(1).map(in_loop); } } @@ -229,7 +233,7 @@ fn late_bound_lifetimes() { { } map_str(|s| take_asref_path(s)); - map_str_to_path(std::convert::AsRef::as_ref); + map_str_to_path(|s| s.as_ref()); } mod type_param_bound { @@ -275,3 +279,15 @@ mod bind_by_ref { Some(A).map(|ref a| B::from(a)); } } + +// #7812 False positive on coerced closure +fn coerced_closure() { + fn function_returning_unit(f: F) {} + function_returning_unit(|x| std::process::exit(x)); + + fn arr() -> &'static [u8; 0] { + &[] + } + fn slice_fn(_: impl FnOnce() -> &'static [u8]) {} + slice_fn(|| arr()); +} diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index 5fdf7fb9771..a1a9c0dfbf3 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -211,6 +211,10 @@ fn mutable_closure_in_loop() { let mut closure = |n| value += n; for _ in 0..5 { Some(1).map(|n| closure(n)); + + let mut value = 0; + let mut in_loop = |n| value += n; + Some(1).map(|n| in_loop(n)); } } @@ -275,3 +279,15 @@ mod bind_by_ref { Some(A).map(|ref a| B::from(a)); } } + +// #7812 False positive on coerced closure +fn coerced_closure() { + fn function_returning_unit(f: F) {} + function_returning_unit(|x| std::process::exit(x)); + + fn arr() -> &'static [u8; 0] { + &[] + } + fn slice_fn(_: impl FnOnce() -> &'static [u8]) {} + slice_fn(|| arr()); +} diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index cda84982c9b..bf2e97e744a 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -24,12 +24,6 @@ error: redundant closure LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted | ^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `below` -error: redundant closure - --> $DIR/eta.rs:40:27 - | -LL | let e = Some(1u8).map(|a| divergent(a)); - | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `divergent` - error: redundant closure --> $DIR/eta.rs:41:27 | @@ -117,10 +111,10 @@ LL | Some(1).map(|n| closure(n)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure` error: redundant closure - --> $DIR/eta.rs:232:21 + --> $DIR/eta.rs:217:21 | -LL | map_str_to_path(|s| s.as_ref()); - | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::convert::AsRef::as_ref` +LL | Some(1).map(|n| in_loop(n)); + | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `in_loop` -error: aborting due to 20 previous errors +error: aborting due to 19 previous errors diff --git a/tests/ui/extra_unused_lifetimes.rs b/tests/ui/extra_unused_lifetimes.rs index 150acfbfee7..f76127a7105 100644 --- a/tests/ui/extra_unused_lifetimes.rs +++ b/tests/ui/extra_unused_lifetimes.rs @@ -72,4 +72,46 @@ mod issue4291 { } } +mod issue6437 { + pub struct Scalar; + + impl<'a> std::ops::AddAssign<&Scalar> for &mut Scalar { + fn add_assign(&mut self, _rhs: &Scalar) { + unimplemented!(); + } + } + + impl<'b> Scalar { + pub fn something<'c>() -> Self { + Self + } + } +} + +// https://github.com/rust-lang/rust-clippy/pull/8737#pullrequestreview-951268213 +mod first_case { + use serde::de::Visitor; + pub trait Expected { + fn fmt(&self, formatter: &mut std::fmt::Formatter); + } + + impl<'de, T> Expected for T + where + T: Visitor<'de>, + { + fn fmt(&self, formatter: &mut std::fmt::Formatter) {} + } +} + +// https://github.com/rust-lang/rust-clippy/pull/8737#pullrequestreview-951268213 +mod second_case { + pub trait Source { + fn hey(); + } + + impl<'a, T: Source + ?Sized + 'a> Source for Box { + fn hey() {} + } +} + fn main() {} diff --git a/tests/ui/extra_unused_lifetimes.stderr b/tests/ui/extra_unused_lifetimes.stderr index 9143fb2c208..fcc12d4ce14 100644 --- a/tests/ui/extra_unused_lifetimes.stderr +++ b/tests/ui/extra_unused_lifetimes.stderr @@ -18,5 +18,23 @@ error: this lifetime isn't used in the function definition LL | fn unused_lt<'a>(x: u8) {} | ^^ -error: aborting due to 3 previous errors +error: this lifetime isn't used in the impl + --> $DIR/extra_unused_lifetimes.rs:78:10 + | +LL | impl<'a> std::ops::AddAssign<&Scalar> for &mut Scalar { + | ^^ + +error: this lifetime isn't used in the impl + --> $DIR/extra_unused_lifetimes.rs:84:10 + | +LL | impl<'b> Scalar { + | ^^ + +error: this lifetime isn't used in the function definition + --> $DIR/extra_unused_lifetimes.rs:85:26 + | +LL | pub fn something<'c>() -> Self { + | ^^ + +error: aborting due to 6 previous errors diff --git a/tests/ui/format_push_string.rs b/tests/ui/format_push_string.rs new file mode 100644 index 00000000000..4db13d650eb --- /dev/null +++ b/tests/ui/format_push_string.rs @@ -0,0 +1,7 @@ +#![warn(clippy::format_push_string)] + +fn main() { + let mut string = String::new(); + string += &format!("{:?}", 1234); + string.push_str(&format!("{:?}", 5678)); +} diff --git a/tests/ui/format_push_string.stderr b/tests/ui/format_push_string.stderr new file mode 100644 index 00000000000..953784bcc06 --- /dev/null +++ b/tests/ui/format_push_string.stderr @@ -0,0 +1,19 @@ +error: `format!(..)` appended to existing `String` + --> $DIR/format_push_string.rs:5:5 + | +LL | string += &format!("{:?}", 1234); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::format-push-string` implied by `-D warnings` + = help: consider using `write!` to avoid the extra allocation + +error: `format!(..)` appended to existing `String` + --> $DIR/format_push_string.rs:6:5 + | +LL | string.push_str(&format!("{:?}", 5678)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using `write!` to avoid the extra allocation + +error: aborting due to 2 previous errors + diff --git a/tests/ui/identity_op.rs b/tests/ui/identity_op.rs index edc3fe1aec1..fec54d00ccb 100644 --- a/tests/ui/identity_op.rs +++ b/tests/ui/identity_op.rs @@ -1,3 +1,5 @@ +use std::fmt::Write as _; + const ONE: i64 = 1; const NEG_ONE: i64 = -1; const ZERO: i64 = 0; @@ -7,7 +9,7 @@ struct A(String); impl std::ops::Shl for A { type Output = A; fn shl(mut self, other: i32) -> Self { - self.0.push_str(&format!("{}", other)); + let _ = write!(self.0, "{}", other); self } } @@ -75,4 +77,34 @@ fn main() { (x + 1) % 3; // no error 4 % 3; // no error 4 % -3; // no error + + // See #8724 + let a = 0; + let b = true; + 0 + if b { 1 } else { 2 }; + 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }; // no error + 0 + match a { 0 => 10, _ => 20 }; + 0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 }; // no error + 0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 }; // no error + 0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 }; // no error + + 0 + if b { 0 + 1 } else { 2 }; + 0 + match a { 0 => 0 + 10, _ => 20 }; + 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 }; + + let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 }; + let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 }; + + 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 } + 0; + + 0 + { a } + 3; // no error + 0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 }; // no error + + fn f(_: i32) { + todo!(); + } + f(1 * a + { 8 * 5 }); + f(0 + if b { 1 } else { 2 } + 3); // no error + const _: i32 = { 2 * 4 } + 0 + 3; + const _: i32 = 0 + { 1 + 2 * 3 } + 3; // no error } diff --git a/tests/ui/identity_op.stderr b/tests/ui/identity_op.stderr index 706f01a3dd6..d8cb65839cb 100644 --- a/tests/ui/identity_op.stderr +++ b/tests/ui/identity_op.stderr @@ -1,5 +1,5 @@ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:37:5 + --> $DIR/identity_op.rs:39:5 | LL | x + 0; | ^^^^^ @@ -7,106 +7,196 @@ LL | x + 0; = note: `-D clippy::identity-op` implied by `-D warnings` error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:38:5 + --> $DIR/identity_op.rs:40:5 | LL | x + (1 - 1); | ^^^^^^^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:40:5 + --> $DIR/identity_op.rs:42:5 | LL | 0 + x; | ^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:43:5 + --> $DIR/identity_op.rs:45:5 | LL | x | (0); | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:46:5 + --> $DIR/identity_op.rs:48:5 | LL | x * 1; | ^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:47:5 + --> $DIR/identity_op.rs:49:5 | LL | 1 * x; | ^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:53:5 + --> $DIR/identity_op.rs:55:5 | LL | -1 & x; | ^^^^^^ error: the operation is ineffective. Consider reducing it to `u` - --> $DIR/identity_op.rs:56:5 + --> $DIR/identity_op.rs:58:5 | LL | u & 255; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `42` - --> $DIR/identity_op.rs:59:5 + --> $DIR/identity_op.rs:61:5 | LL | 42 << 0; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:60:5 + --> $DIR/identity_op.rs:62:5 | LL | 1 >> 0; | ^^^^^^ error: the operation is ineffective. Consider reducing it to `42` - --> $DIR/identity_op.rs:61:5 + --> $DIR/identity_op.rs:63:5 | LL | 42 >> 0; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `&x` - --> $DIR/identity_op.rs:62:5 + --> $DIR/identity_op.rs:64:5 | LL | &x >> 0; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:63:5 + --> $DIR/identity_op.rs:65:5 | LL | x >> &0; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `2` - --> $DIR/identity_op.rs:70:5 + --> $DIR/identity_op.rs:72:5 | LL | 2 % 3; | ^^^^^ error: the operation is ineffective. Consider reducing it to `-2` - --> $DIR/identity_op.rs:71:5 + --> $DIR/identity_op.rs:73:5 | LL | -2 % 3; | ^^^^^^ error: the operation is ineffective. Consider reducing it to `2` - --> $DIR/identity_op.rs:72:5 + --> $DIR/identity_op.rs:74:5 | LL | 2 % -3 + x; | ^^^^^^ error: the operation is ineffective. Consider reducing it to `-2` - --> $DIR/identity_op.rs:73:5 + --> $DIR/identity_op.rs:75:5 | LL | -2 % -3 + x; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:74:9 + --> $DIR/identity_op.rs:76:9 | LL | x + 1 % 3; | ^^^^^ -error: aborting due to 18 previous errors +error: the operation is ineffective. Consider reducing it to `if b { 1 } else { 2 }` + --> $DIR/identity_op.rs:84:5 + | +LL | 0 + if b { 1 } else { 2 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `match a { 0 => 10, _ => 20 }` + --> $DIR/identity_op.rs:86:5 + | +LL | 0 + match a { 0 => 10, _ => 20 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `if b { 0 + 1 } else { 2 }` + --> $DIR/identity_op.rs:91:5 + | +LL | 0 + if b { 0 + 1 } else { 2 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:91:16 + | +LL | 0 + if b { 0 + 1 } else { 2 }; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `match a { 0 => 0 + 10, _ => 20 }` + --> $DIR/identity_op.rs:92:5 + | +LL | 0 + match a { 0 => 0 + 10, _ => 20 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `10` + --> $DIR/identity_op.rs:92:25 + | +LL | 0 + match a { 0 => 0 + 10, _ => 20 }; + | ^^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:93:16 + | +LL | 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 }; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `30` + --> $DIR/identity_op.rs:93:52 + | +LL | 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 }; + | ^^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:95:20 + | +LL | let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 }; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:95:52 + | +LL | let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 }; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:96:23 + | +LL | let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 }; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:96:58 + | +LL | let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 }; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` + --> $DIR/identity_op.rs:98:5 + | +LL | 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 } + 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `a` + --> $DIR/identity_op.rs:106:7 + | +LL | f(1 * a + { 8 * 5 }); + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `{ 2 * 4 }` + --> $DIR/identity_op.rs:108:20 + | +LL | const _: i32 = { 2 * 4 } + 0 + 3; + | ^^^^^^^^^^^^^ + +error: aborting due to 33 previous errors diff --git a/tests/ui/impl.rs b/tests/ui/impl.rs index 39443775015..aea52a852f9 100644 --- a/tests/ui/impl.rs +++ b/tests/ui/impl.rs @@ -1,4 +1,4 @@ -#![allow(dead_code)] +#![allow(dead_code, clippy::extra_unused_lifetimes)] #![warn(clippy::multiple_inherent_impl)] struct MyStruct; diff --git a/tests/ui/is_digit_ascii_radix.fixed b/tests/ui/is_digit_ascii_radix.fixed new file mode 100644 index 00000000000..c0ba647d707 --- /dev/null +++ b/tests/ui/is_digit_ascii_radix.fixed @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::is_digit_ascii_radix)] + +const TEN: u32 = 10; + +fn main() { + let c: char = '6'; + + // Should trigger the lint. + let _ = c.is_ascii_digit(); + let _ = c.is_ascii_hexdigit(); + let _ = c.is_ascii_hexdigit(); + + // Should not trigger the lint. + let _ = c.is_digit(11); + let _ = c.is_digit(TEN); +} diff --git a/tests/ui/is_digit_ascii_radix.rs b/tests/ui/is_digit_ascii_radix.rs new file mode 100644 index 00000000000..68e3f3243d9 --- /dev/null +++ b/tests/ui/is_digit_ascii_radix.rs @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::is_digit_ascii_radix)] + +const TEN: u32 = 10; + +fn main() { + let c: char = '6'; + + // Should trigger the lint. + let _ = c.is_digit(10); + let _ = c.is_digit(16); + let _ = c.is_digit(0x10); + + // Should not trigger the lint. + let _ = c.is_digit(11); + let _ = c.is_digit(TEN); +} diff --git a/tests/ui/is_digit_ascii_radix.stderr b/tests/ui/is_digit_ascii_radix.stderr new file mode 100644 index 00000000000..dc5cb2913ae --- /dev/null +++ b/tests/ui/is_digit_ascii_radix.stderr @@ -0,0 +1,22 @@ +error: use of `char::is_digit` with literal radix of 10 + --> $DIR/is_digit_ascii_radix.rs:11:13 + | +LL | let _ = c.is_digit(10); + | ^^^^^^^^^^^^^^ help: try: `c.is_ascii_digit()` + | + = note: `-D clippy::is-digit-ascii-radix` implied by `-D warnings` + +error: use of `char::is_digit` with literal radix of 16 + --> $DIR/is_digit_ascii_radix.rs:12:13 + | +LL | let _ = c.is_digit(16); + | ^^^^^^^^^^^^^^ help: try: `c.is_ascii_hexdigit()` + +error: use of `char::is_digit` with literal radix of 16 + --> $DIR/is_digit_ascii_radix.rs:13:13 + | +LL | let _ = c.is_digit(0x10); + | ^^^^^^^^^^^^^^^^ help: try: `c.is_ascii_hexdigit()` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/iter_overeager_cloned.fixed b/tests/ui/iter_overeager_cloned.fixed index 56761ebbcb8..7c2b05d837b 100644 --- a/tests/ui/iter_overeager_cloned.fixed +++ b/tests/ui/iter_overeager_cloned.fixed @@ -1,6 +1,6 @@ // run-rustfix #![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] -#![allow(dead_code)] +#![allow(dead_code, clippy::let_unit_value)] fn main() { let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()]; diff --git a/tests/ui/iter_overeager_cloned.rs b/tests/ui/iter_overeager_cloned.rs index 98321d889b5..f2d0b155d2c 100644 --- a/tests/ui/iter_overeager_cloned.rs +++ b/tests/ui/iter_overeager_cloned.rs @@ -1,6 +1,6 @@ // run-rustfix #![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] -#![allow(dead_code)] +#![allow(dead_code, clippy::let_unit_value)] fn main() { let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()]; diff --git a/tests/ui/iter_with_drain.fixed b/tests/ui/iter_with_drain.fixed index aea4dba9dd5..0330d554926 100644 --- a/tests/ui/iter_with_drain.fixed +++ b/tests/ui/iter_with_drain.fixed @@ -39,6 +39,15 @@ fn should_not_help() { let _: Vec<_> = b.drain(0..a.len()).collect(); } +fn _closed_range(mut x: Vec) { + let _: Vec = x.drain(0..=x.len()).collect(); +} + +fn _with_mut(x: &mut Vec, y: &mut VecDeque) { + let _: Vec = x.drain(..).collect(); + let _: Vec = y.drain(..).collect(); +} + #[derive(Default)] struct Bomb { fire: Vec, diff --git a/tests/ui/iter_with_drain.rs b/tests/ui/iter_with_drain.rs index 271878cffb4..993936fb8de 100644 --- a/tests/ui/iter_with_drain.rs +++ b/tests/ui/iter_with_drain.rs @@ -39,6 +39,15 @@ fn should_not_help() { let _: Vec<_> = b.drain(0..a.len()).collect(); } +fn _closed_range(mut x: Vec) { + let _: Vec = x.drain(0..=x.len()).collect(); +} + +fn _with_mut(x: &mut Vec, y: &mut VecDeque) { + let _: Vec = x.drain(..).collect(); + let _: Vec = y.drain(..).collect(); +} + #[derive(Default)] struct Bomb { fire: Vec, diff --git a/tests/ui/let_underscore_drop.rs b/tests/ui/let_underscore_drop.rs index 50744f81c3c..11b50492ab2 100644 --- a/tests/ui/let_underscore_drop.rs +++ b/tests/ui/let_underscore_drop.rs @@ -1,4 +1,5 @@ #![warn(clippy::let_underscore_drop)] +#![allow(clippy::let_unit_value)] struct Droppable; diff --git a/tests/ui/let_underscore_drop.stderr b/tests/ui/let_underscore_drop.stderr index 66069e0c5e1..ee7bbe995f1 100644 --- a/tests/ui/let_underscore_drop.stderr +++ b/tests/ui/let_underscore_drop.stderr @@ -1,5 +1,5 @@ error: non-binding `let` on a type that implements `Drop` - --> $DIR/let_underscore_drop.rs:16:5 + --> $DIR/let_underscore_drop.rs:17:5 | LL | let _ = Box::new(()); | ^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let _ = Box::new(()); = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` error: non-binding `let` on a type that implements `Drop` - --> $DIR/let_underscore_drop.rs:17:5 + --> $DIR/let_underscore_drop.rs:18:5 | LL | let _ = Droppable; | ^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | let _ = Droppable; = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` error: non-binding `let` on a type that implements `Drop` - --> $DIR/let_underscore_drop.rs:18:5 + --> $DIR/let_underscore_drop.rs:19:5 | LL | let _ = Some(Droppable); | ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/let_unit.fixed b/tests/ui/let_unit.fixed index f398edc23cb..e72b7462325 100644 --- a/tests/ui/let_unit.fixed +++ b/tests/ui/let_unit.fixed @@ -61,3 +61,55 @@ fn multiline_sugg() { #[derive(Copy, Clone)] pub struct ContainsUnit(()); // should be fine + +fn _returns_generic() { + fn f() -> T { + unimplemented!() + } + fn f2(_: T) -> U { + unimplemented!() + } + fn f3(x: T) -> T { + x + } + fn f4(mut x: Vec) -> T { + x.pop().unwrap() + } + + let _: () = f(); // Ok + let _: () = f(); // Lint. + + let _: () = f2(0i32); // Ok + let _: () = f2(0i32); // Lint. + + f3(()); // Lint + f3(()); // Lint + + f4(vec![()]); // Lint + f4(vec![()]); // Lint + + // Ok + let _: () = { + let x = 5; + f2(x) + }; + + let _: () = if true { f() } else { f2(0) }; // Ok + let _: () = if true { f() } else { f2(0) }; // Lint + + // Ok + let _: () = match Some(0) { + None => f2(1), + Some(0) => f(), + Some(1) => f2(3), + Some(_) => f2('x'), + }; + + // Lint + match Some(0) { + None => f2(1), + Some(0) => f(), + Some(1) => f2(3), + Some(_) => (), + }; +} diff --git a/tests/ui/let_unit.rs b/tests/ui/let_unit.rs index af5b1fb2ac7..47ee0a76724 100644 --- a/tests/ui/let_unit.rs +++ b/tests/ui/let_unit.rs @@ -61,3 +61,55 @@ fn multiline_sugg() { #[derive(Copy, Clone)] pub struct ContainsUnit(()); // should be fine + +fn _returns_generic() { + fn f() -> T { + unimplemented!() + } + fn f2(_: T) -> U { + unimplemented!() + } + fn f3(x: T) -> T { + x + } + fn f4(mut x: Vec) -> T { + x.pop().unwrap() + } + + let _: () = f(); // Ok + let x: () = f(); // Lint. + + let _: () = f2(0i32); // Ok + let x: () = f2(0i32); // Lint. + + let _: () = f3(()); // Lint + let x: () = f3(()); // Lint + + let _: () = f4(vec![()]); // Lint + let x: () = f4(vec![()]); // Lint + + // Ok + let _: () = { + let x = 5; + f2(x) + }; + + let _: () = if true { f() } else { f2(0) }; // Ok + let x: () = if true { f() } else { f2(0) }; // Lint + + // Ok + let _: () = match Some(0) { + None => f2(1), + Some(0) => f(), + Some(1) => f2(3), + Some(_) => f2('x'), + }; + + // Lint + let _: () = match Some(0) { + None => f2(1), + Some(0) => f(), + Some(1) => f2(3), + Some(_) => (), + }; +} diff --git a/tests/ui/let_unit.stderr b/tests/ui/let_unit.stderr index f2600c6c228..13ec11a6d33 100644 --- a/tests/ui/let_unit.stderr +++ b/tests/ui/let_unit.stderr @@ -34,5 +34,74 @@ LL + .map(|_| ()) LL + .next() ... -error: aborting due to 3 previous errors +error: this let-binding has unit value + --> $DIR/let_unit.rs:80:5 + | +LL | let x: () = f(); // Lint. + | ^^^^-^^^^^^^^^^^ + | | + | help: use a wild (`_`) binding: `_` + +error: this let-binding has unit value + --> $DIR/let_unit.rs:83:5 + | +LL | let x: () = f2(0i32); // Lint. + | ^^^^-^^^^^^^^^^^^^^^^ + | | + | help: use a wild (`_`) binding: `_` + +error: this let-binding has unit value + --> $DIR/let_unit.rs:85:5 + | +LL | let _: () = f3(()); // Lint + | ^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f3(());` + +error: this let-binding has unit value + --> $DIR/let_unit.rs:86:5 + | +LL | let x: () = f3(()); // Lint + | ^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f3(());` + +error: this let-binding has unit value + --> $DIR/let_unit.rs:88:5 + | +LL | let _: () = f4(vec![()]); // Lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f4(vec![()]);` + +error: this let-binding has unit value + --> $DIR/let_unit.rs:89:5 + | +LL | let x: () = f4(vec![()]); // Lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f4(vec![()]);` + +error: this let-binding has unit value + --> $DIR/let_unit.rs:98:5 + | +LL | let x: () = if true { f() } else { f2(0) }; // Lint + | ^^^^-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: use a wild (`_`) binding: `_` + +error: this let-binding has unit value + --> $DIR/let_unit.rs:109:5 + | +LL | / let _: () = match Some(0) { +LL | | None => f2(1), +LL | | Some(0) => f(), +LL | | Some(1) => f2(3), +LL | | Some(_) => (), +LL | | }; + | |______^ + | +help: omit the `let` binding + | +LL ~ match Some(0) { +LL + None => f2(1), +LL + Some(0) => f(), +LL + Some(1) => f2(3), +LL + Some(_) => (), +LL + }; + | + +error: aborting due to 11 previous errors diff --git a/tests/ui/manual_bits.fixed b/tests/ui/manual_bits.fixed index 4f1b19b75b8..386360dbdcd 100644 --- a/tests/ui/manual_bits.fixed +++ b/tests/ui/manual_bits.fixed @@ -1,38 +1,44 @@ // run-rustfix #![warn(clippy::manual_bits)] -#![allow(clippy::no_effect, path_statements, unused_must_use, clippy::unnecessary_operation)] +#![allow( + clippy::no_effect, + clippy::useless_conversion, + path_statements, + unused_must_use, + clippy::unnecessary_operation +)] use std::mem::{size_of, size_of_val}; fn main() { - i8::BITS; - i16::BITS; - i32::BITS; - i64::BITS; - i128::BITS; - isize::BITS; + i8::BITS as usize; + i16::BITS as usize; + i32::BITS as usize; + i64::BITS as usize; + i128::BITS as usize; + isize::BITS as usize; - u8::BITS; - u16::BITS; - u32::BITS; - u64::BITS; - u128::BITS; - usize::BITS; + u8::BITS as usize; + u16::BITS as usize; + u32::BITS as usize; + u64::BITS as usize; + u128::BITS as usize; + usize::BITS as usize; - i8::BITS; - i16::BITS; - i32::BITS; - i64::BITS; - i128::BITS; - isize::BITS; + i8::BITS as usize; + i16::BITS as usize; + i32::BITS as usize; + i64::BITS as usize; + i128::BITS as usize; + isize::BITS as usize; - u8::BITS; - u16::BITS; - u32::BITS; - u64::BITS; - u128::BITS; - usize::BITS; + u8::BITS as usize; + u16::BITS as usize; + u32::BITS as usize; + u64::BITS as usize; + u128::BITS as usize; + usize::BITS as usize; size_of::() * 4; 4 * size_of::(); @@ -42,7 +48,12 @@ fn main() { size_of_val(&0u32) * 8; type Word = u32; - Word::BITS; + Word::BITS as usize; type Bool = bool; size_of::() * 8; + + let _: u32 = u128::BITS as u32; + let _: u32 = u128::BITS.try_into().unwrap(); + let _ = (u128::BITS as usize).pow(5); + let _ = &(u128::BITS as usize); } diff --git a/tests/ui/manual_bits.rs b/tests/ui/manual_bits.rs index f8a01313e6a..62638f047eb 100644 --- a/tests/ui/manual_bits.rs +++ b/tests/ui/manual_bits.rs @@ -1,7 +1,13 @@ // run-rustfix #![warn(clippy::manual_bits)] -#![allow(clippy::no_effect, path_statements, unused_must_use, clippy::unnecessary_operation)] +#![allow( + clippy::no_effect, + clippy::useless_conversion, + path_statements, + unused_must_use, + clippy::unnecessary_operation +)] use std::mem::{size_of, size_of_val}; @@ -45,4 +51,9 @@ fn main() { size_of::() * 8; type Bool = bool; size_of::() * 8; + + let _: u32 = (size_of::() * 8) as u32; + let _: u32 = (size_of::() * 8).try_into().unwrap(); + let _ = (size_of::() * 8).pow(5); + let _ = &(size_of::() * 8); } diff --git a/tests/ui/manual_bits.stderr b/tests/ui/manual_bits.stderr index c4f5af2dcb0..69c591a203d 100644 --- a/tests/ui/manual_bits.stderr +++ b/tests/ui/manual_bits.stderr @@ -1,154 +1,178 @@ error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:9:5 + --> $DIR/manual_bits.rs:15:5 | LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS` + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS as usize` | = note: `-D clippy::manual-bits` implied by `-D warnings` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:10:5 - | -LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS` - -error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:11:5 - | -LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS` - -error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:12:5 - | -LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS` - -error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:13:5 - | -LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS` - -error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:14:5 - | -LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS` - error: usage of `mem::size_of::()` to obtain the size of `T` in bits --> $DIR/manual_bits.rs:16:5 | -LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS` +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits --> $DIR/manual_bits.rs:17:5 | -LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS` +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits --> $DIR/manual_bits.rs:18:5 | -LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS` +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits --> $DIR/manual_bits.rs:19:5 | -LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS` +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits --> $DIR/manual_bits.rs:20:5 | -LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:21:5 + --> $DIR/manual_bits.rs:22:5 | -LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS` +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits --> $DIR/manual_bits.rs:23:5 | -LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS` +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits --> $DIR/manual_bits.rs:24:5 | -LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS` +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits --> $DIR/manual_bits.rs:25:5 | -LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS` +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits --> $DIR/manual_bits.rs:26:5 | -LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS` +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits --> $DIR/manual_bits.rs:27:5 | -LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS` +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:28:5 + --> $DIR/manual_bits.rs:29:5 | -LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS` +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits --> $DIR/manual_bits.rs:30:5 | -LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS` +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits --> $DIR/manual_bits.rs:31:5 | -LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS` +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits --> $DIR/manual_bits.rs:32:5 | -LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS` +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits --> $DIR/manual_bits.rs:33:5 | -LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS` +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits --> $DIR/manual_bits.rs:34:5 | -LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:35:5 + --> $DIR/manual_bits.rs:36:5 + | +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS as usize` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:37:5 + | +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS as usize` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:38:5 + | +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS as usize` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:39:5 + | +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS as usize` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:40:5 + | +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS as usize` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:41:5 | LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS` + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:45:5 + --> $DIR/manual_bits.rs:51:5 | LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `Word::BITS` + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `Word::BITS as usize` -error: aborting due to 25 previous errors +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:55:18 + | +LL | let _: u32 = (size_of::() * 8) as u32; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:56:18 + | +LL | let _: u32 = (size_of::() * 8).try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:57:13 + | +LL | let _ = (size_of::() * 8).pow(5); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(u128::BITS as usize)` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:58:14 + | +LL | let _ = &(size_of::() * 8); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(u128::BITS as usize)` + +error: aborting due to 29 previous errors diff --git a/tests/ui/manual_non_exhaustive_enum.rs b/tests/ui/manual_non_exhaustive_enum.rs new file mode 100644 index 00000000000..f23c6d69b4c --- /dev/null +++ b/tests/ui/manual_non_exhaustive_enum.rs @@ -0,0 +1,78 @@ +#![warn(clippy::manual_non_exhaustive)] +#![allow(unused)] + +enum E { + A, + B, + #[doc(hidden)] + _C, +} + +// user forgot to remove the marker +#[non_exhaustive] +enum Ep { + A, + B, + #[doc(hidden)] + _C, +} + +// marker variant does not have doc hidden attribute, should be ignored +enum NoDocHidden { + A, + B, + _C, +} + +// name of variant with doc hidden does not start with underscore, should be ignored +enum NoUnderscore { + A, + B, + #[doc(hidden)] + C, +} + +// variant with doc hidden is not unit, should be ignored +enum NotUnit { + A, + B, + #[doc(hidden)] + _C(bool), +} + +// variant with doc hidden is the only one, should be ignored +enum OnlyMarker { + #[doc(hidden)] + _A, +} + +// variant with multiple markers, should be ignored +enum MultipleMarkers { + A, + #[doc(hidden)] + _B, + #[doc(hidden)] + _C, +} + +// already non_exhaustive and no markers, should be ignored +#[non_exhaustive] +enum NonExhaustive { + A, + B, +} + +// marked is used, don't lint +enum UsedHidden { + #[doc(hidden)] + _A, + B, + C, +} +fn foo(x: &mut UsedHidden) { + if matches!(*x, UsedHidden::B) { + *x = UsedHidden::_A; + } +} + +fn main() {} diff --git a/tests/ui/manual_non_exhaustive_enum.stderr b/tests/ui/manual_non_exhaustive_enum.stderr new file mode 100644 index 00000000000..317a45d2cbd --- /dev/null +++ b/tests/ui/manual_non_exhaustive_enum.stderr @@ -0,0 +1,41 @@ +error: this seems like a manual implementation of the non-exhaustive pattern + --> $DIR/manual_non_exhaustive_enum.rs:4:1 + | +LL | enum E { + | ^----- + | | + | _help: add the attribute: `#[non_exhaustive] enum E` + | | +LL | | A, +LL | | B, +LL | | #[doc(hidden)] +LL | | _C, +LL | | } + | |_^ + | + = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings` +help: remove this variant + --> $DIR/manual_non_exhaustive_enum.rs:8:5 + | +LL | _C, + | ^^ + +error: this seems like a manual implementation of the non-exhaustive pattern + --> $DIR/manual_non_exhaustive_enum.rs:13:1 + | +LL | / enum Ep { +LL | | A, +LL | | B, +LL | | #[doc(hidden)] +LL | | _C, +LL | | } + | |_^ + | +help: remove this variant + --> $DIR/manual_non_exhaustive_enum.rs:17:5 + | +LL | _C, + | ^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/manual_non_exhaustive.rs b/tests/ui/manual_non_exhaustive_struct.rs similarity index 58% rename from tests/ui/manual_non_exhaustive.rs rename to tests/ui/manual_non_exhaustive_struct.rs index 7a788f48520..498eee4447b 100644 --- a/tests/ui/manual_non_exhaustive.rs +++ b/tests/ui/manual_non_exhaustive_struct.rs @@ -1,69 +1,6 @@ #![warn(clippy::manual_non_exhaustive)] #![allow(unused)] -mod enums { - enum E { - A, - B, - #[doc(hidden)] - _C, - } - - // user forgot to remove the marker - #[non_exhaustive] - enum Ep { - A, - B, - #[doc(hidden)] - _C, - } - - // marker variant does not have doc hidden attribute, should be ignored - enum NoDocHidden { - A, - B, - _C, - } - - // name of variant with doc hidden does not start with underscore, should be ignored - enum NoUnderscore { - A, - B, - #[doc(hidden)] - C, - } - - // variant with doc hidden is not unit, should be ignored - enum NotUnit { - A, - B, - #[doc(hidden)] - _C(bool), - } - - // variant with doc hidden is the only one, should be ignored - enum OnlyMarker { - #[doc(hidden)] - _A, - } - - // variant with multiple markers, should be ignored - enum MultipleMarkers { - A, - #[doc(hidden)] - _B, - #[doc(hidden)] - _C, - } - - // already non_exhaustive and no markers, should be ignored - #[non_exhaustive] - enum NonExhaustive { - A, - B, - } -} - mod structs { struct S { pub a: i32, diff --git a/tests/ui/manual_non_exhaustive.stderr b/tests/ui/manual_non_exhaustive_struct.stderr similarity index 53% rename from tests/ui/manual_non_exhaustive.stderr rename to tests/ui/manual_non_exhaustive_struct.stderr index 613c5e8ca1d..e0766c17b75 100644 --- a/tests/ui/manual_non_exhaustive.stderr +++ b/tests/ui/manual_non_exhaustive_struct.stderr @@ -1,44 +1,5 @@ error: this seems like a manual implementation of the non-exhaustive pattern - --> $DIR/manual_non_exhaustive.rs:5:5 - | -LL | enum E { - | ^----- - | | - | _____help: add the attribute: `#[non_exhaustive] enum E` - | | -LL | | A, -LL | | B, -LL | | #[doc(hidden)] -LL | | _C, -LL | | } - | |_____^ - | - = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings` -help: remove this variant - --> $DIR/manual_non_exhaustive.rs:9:9 - | -LL | _C, - | ^^ - -error: this seems like a manual implementation of the non-exhaustive pattern - --> $DIR/manual_non_exhaustive.rs:14:5 - | -LL | / enum Ep { -LL | | A, -LL | | B, -LL | | #[doc(hidden)] -LL | | _C, -LL | | } - | |_____^ - | -help: remove this variant - --> $DIR/manual_non_exhaustive.rs:18:9 - | -LL | _C, - | ^^ - -error: this seems like a manual implementation of the non-exhaustive pattern - --> $DIR/manual_non_exhaustive.rs:68:5 + --> $DIR/manual_non_exhaustive_struct.rs:5:5 | LL | struct S { | ^------- @@ -51,14 +12,15 @@ LL | | _c: (), LL | | } | |_____^ | + = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings` help: remove this field - --> $DIR/manual_non_exhaustive.rs:71:9 + --> $DIR/manual_non_exhaustive_struct.rs:8:9 | LL | _c: (), | ^^^^^^ error: this seems like a manual implementation of the non-exhaustive pattern - --> $DIR/manual_non_exhaustive.rs:76:5 + --> $DIR/manual_non_exhaustive_struct.rs:13:5 | LL | / struct Sp { LL | | pub a: i32, @@ -68,13 +30,13 @@ LL | | } | |_____^ | help: remove this field - --> $DIR/manual_non_exhaustive.rs:79:9 + --> $DIR/manual_non_exhaustive_struct.rs:16:9 | LL | _c: (), | ^^^^^^ error: this seems like a manual implementation of the non-exhaustive pattern - --> $DIR/manual_non_exhaustive.rs:117:5 + --> $DIR/manual_non_exhaustive_struct.rs:54:5 | LL | struct T(pub i32, pub i32, ()); | --------^^^^^^^^^^^^^^^^^^^^^^^ @@ -82,22 +44,22 @@ LL | struct T(pub i32, pub i32, ()); | help: add the attribute: `#[non_exhaustive] struct T` | help: remove this field - --> $DIR/manual_non_exhaustive.rs:117:32 + --> $DIR/manual_non_exhaustive_struct.rs:54:32 | LL | struct T(pub i32, pub i32, ()); | ^^ error: this seems like a manual implementation of the non-exhaustive pattern - --> $DIR/manual_non_exhaustive.rs:121:5 + --> $DIR/manual_non_exhaustive_struct.rs:58:5 | LL | struct Tp(pub i32, pub i32, ()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove this field - --> $DIR/manual_non_exhaustive.rs:121:33 + --> $DIR/manual_non_exhaustive_struct.rs:58:33 | LL | struct Tp(pub i32, pub i32, ()); | ^^ -error: aborting due to 6 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/manual_split_once.fixed b/tests/ui/manual_split_once.fixed index d5113df569a..c7ca770434a 100644 --- a/tests/ui/manual_split_once.fixed +++ b/tests/ui/manual_split_once.fixed @@ -2,7 +2,7 @@ #![feature(custom_inner_attributes)] #![warn(clippy::manual_split_once)] -#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::needless_splitn)] +#![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)] extern crate itertools; @@ -10,27 +10,25 @@ extern crate itertools; use itertools::Itertools; fn main() { - let _ = Some("key=value".split_once('=').map_or("key=value", |x| x.0)); let _ = "key=value".splitn(2, '=').nth(2); - let _ = "key=value".split_once('=').map_or("key=value", |x| x.0); - let _ = "key=value".split_once('=').map_or("key=value", |x| x.0); let _ = "key=value".split_once('=').unwrap().1; let _ = "key=value".split_once('=').unwrap().1; let (_, _) = "key=value".split_once('=').unwrap(); let s = String::from("key=value"); - let _ = s.split_once('=').map_or(&*s, |x| x.0); + let _ = s.split_once('=').unwrap().1; let s = Box::::from("key=value"); - let _ = s.split_once('=').map_or(&*s, |x| x.0); + let _ = s.split_once('=').unwrap().1; let s = &"key=value"; - let _ = s.split_once('=').map_or(*s, |x| x.0); + let _ = s.split_once('=').unwrap().1; fn _f(s: &str) -> Option<&str> { - let _ = s.split_once("key=value").map_or(s, |x| x.0); - let _ = s.split_once("key=value")?.1; - let _ = s.split_once("key=value")?.1; + let _ = s.split_once('=')?.1; + let _ = s.split_once('=')?.1; + let _ = s.rsplit_once('=')?.0; + let _ = s.rsplit_once('=')?.0; None } @@ -38,19 +36,112 @@ fn main() { let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap(); // `rsplitn` gives the results in the reverse order of `rsplit_once` - let _ = "key=value".rsplitn(2, '=').next().unwrap(); let _ = "key=value".rsplit_once('=').unwrap().0; - let _ = "key=value".rsplit_once('=').map(|x| x.1); let (_, _) = "key=value".rsplit_once('=').map(|(x, y)| (y, x)).unwrap(); + let _ = s.rsplit_once('=').map(|x| x.0); +} + +fn indirect() -> Option<()> { + let (l, r) = "a.b.c".split_once('.').unwrap(); + + + + let (l, r) = "a.b.c".split_once('.')?; + + + + let (l, r) = "a.b.c".rsplit_once('.').unwrap(); + + + + let (l, r) = "a.b.c".rsplit_once('.')?; + + + + // could lint, currently doesn't + + let mut iter = "a.b.c".splitn(2, '.'); + let other = 1; + let l = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let mut mut_binding = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let tuple = (iter.next()?, iter.next()?); + + // should not lint + + let mut missing_unwrap = "a.b.c".splitn(2, '.'); + let l = missing_unwrap.next(); + let r = missing_unwrap.next(); + + let mut mixed_unrap = "a.b.c".splitn(2, '.'); + let unwrap = mixed_unrap.next().unwrap(); + let question_mark = mixed_unrap.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let same_name = iter.next()?; + let same_name = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let shadows_existing = "d"; + let shadows_existing = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let becomes_shadowed = iter.next()?; + let becomes_shadowed = "d"; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let l = iter.next()?; + let r = iter.next()?; + let third_usage = iter.next()?; + + let mut n_three = "a.b.c".splitn(3, '.'); + let l = n_three.next()?; + let r = n_three.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + { + let in_block = iter.next()?; + } + let r = iter.next()?; + + let mut lacks_binding = "a.b.c".splitn(2, '.'); + let _ = lacks_binding.next()?; + let r = lacks_binding.next()?; + + let mut mapped = "a.b.c".splitn(2, '.').map(|_| "~"); + let l = iter.next()?; + let r = iter.next()?; + + let mut assigned = ""; + let mut iter = "a.b.c".splitn(2, '.'); + let l = iter.next()?; + assigned = iter.next()?; + + None } fn _msrv_1_51() { #![clippy::msrv = "1.51"] - // `str::split_once` was stabilized in 1.16. Do not lint this + // `str::split_once` was stabilized in 1.52. Do not lint this let _ = "key=value".splitn(2, '=').nth(1).unwrap(); + + let mut iter = "a.b.c".splitn(2, '.'); + let a = iter.next().unwrap(); + let b = iter.next().unwrap(); } fn _msrv_1_52() { #![clippy::msrv = "1.52"] let _ = "key=value".split_once('=').unwrap().1; + + let (a, b) = "a.b.c".split_once('.').unwrap(); + + } diff --git a/tests/ui/manual_split_once.rs b/tests/ui/manual_split_once.rs index 80e02952dbd..ee2848a251e 100644 --- a/tests/ui/manual_split_once.rs +++ b/tests/ui/manual_split_once.rs @@ -2,7 +2,7 @@ #![feature(custom_inner_attributes)] #![warn(clippy::manual_split_once)] -#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::needless_splitn)] +#![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)] extern crate itertools; @@ -10,27 +10,25 @@ extern crate itertools; use itertools::Itertools; fn main() { - let _ = "key=value".splitn(2, '=').next(); let _ = "key=value".splitn(2, '=').nth(2); - let _ = "key=value".splitn(2, '=').next().unwrap(); - let _ = "key=value".splitn(2, '=').nth(0).unwrap(); let _ = "key=value".splitn(2, '=').nth(1).unwrap(); let _ = "key=value".splitn(2, '=').skip(1).next().unwrap(); let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap(); let s = String::from("key=value"); - let _ = s.splitn(2, '=').next().unwrap(); + let _ = s.splitn(2, '=').nth(1).unwrap(); let s = Box::::from("key=value"); - let _ = s.splitn(2, '=').nth(0).unwrap(); + let _ = s.splitn(2, '=').nth(1).unwrap(); let s = &"key=value"; - let _ = s.splitn(2, '=').skip(0).next().unwrap(); + let _ = s.splitn(2, '=').skip(1).next().unwrap(); fn _f(s: &str) -> Option<&str> { - let _ = s.splitn(2, "key=value").next()?; - let _ = s.splitn(2, "key=value").nth(1)?; - let _ = s.splitn(2, "key=value").skip(1).next()?; + let _ = s.splitn(2, '=').nth(1)?; + let _ = s.splitn(2, '=').skip(1).next()?; + let _ = s.rsplitn(2, '=').nth(1)?; + let _ = s.rsplitn(2, '=').skip(1).next()?; None } @@ -38,19 +36,112 @@ fn main() { let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap(); // `rsplitn` gives the results in the reverse order of `rsplit_once` - let _ = "key=value".rsplitn(2, '=').next().unwrap(); let _ = "key=value".rsplitn(2, '=').nth(1).unwrap(); - let _ = "key=value".rsplitn(2, '=').nth(0); let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap(); + let _ = s.rsplitn(2, '=').nth(1); +} + +fn indirect() -> Option<()> { + let mut iter = "a.b.c".splitn(2, '.'); + let l = iter.next().unwrap(); + let r = iter.next().unwrap(); + + let mut iter = "a.b.c".splitn(2, '.'); + let l = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".rsplitn(2, '.'); + let r = iter.next().unwrap(); + let l = iter.next().unwrap(); + + let mut iter = "a.b.c".rsplitn(2, '.'); + let r = iter.next()?; + let l = iter.next()?; + + // could lint, currently doesn't + + let mut iter = "a.b.c".splitn(2, '.'); + let other = 1; + let l = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let mut mut_binding = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let tuple = (iter.next()?, iter.next()?); + + // should not lint + + let mut missing_unwrap = "a.b.c".splitn(2, '.'); + let l = missing_unwrap.next(); + let r = missing_unwrap.next(); + + let mut mixed_unrap = "a.b.c".splitn(2, '.'); + let unwrap = mixed_unrap.next().unwrap(); + let question_mark = mixed_unrap.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let same_name = iter.next()?; + let same_name = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let shadows_existing = "d"; + let shadows_existing = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let becomes_shadowed = iter.next()?; + let becomes_shadowed = "d"; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let l = iter.next()?; + let r = iter.next()?; + let third_usage = iter.next()?; + + let mut n_three = "a.b.c".splitn(3, '.'); + let l = n_three.next()?; + let r = n_three.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + { + let in_block = iter.next()?; + } + let r = iter.next()?; + + let mut lacks_binding = "a.b.c".splitn(2, '.'); + let _ = lacks_binding.next()?; + let r = lacks_binding.next()?; + + let mut mapped = "a.b.c".splitn(2, '.').map(|_| "~"); + let l = iter.next()?; + let r = iter.next()?; + + let mut assigned = ""; + let mut iter = "a.b.c".splitn(2, '.'); + let l = iter.next()?; + assigned = iter.next()?; + + None } fn _msrv_1_51() { #![clippy::msrv = "1.51"] - // `str::split_once` was stabilized in 1.16. Do not lint this + // `str::split_once` was stabilized in 1.52. Do not lint this let _ = "key=value".splitn(2, '=').nth(1).unwrap(); + + let mut iter = "a.b.c".splitn(2, '.'); + let a = iter.next().unwrap(); + let b = iter.next().unwrap(); } fn _msrv_1_52() { #![clippy::msrv = "1.52"] let _ = "key=value".splitn(2, '=').nth(1).unwrap(); + + let mut iter = "a.b.c".splitn(2, '.'); + let a = iter.next().unwrap(); + let b = iter.next().unwrap(); } diff --git a/tests/ui/manual_split_once.stderr b/tests/ui/manual_split_once.stderr index af9c7a2d41b..2563a6904b7 100644 --- a/tests/ui/manual_split_once.stderr +++ b/tests/ui/manual_split_once.stderr @@ -1,100 +1,213 @@ error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:13:13 + --> $DIR/manual_split_once.rs:14:13 | -LL | let _ = "key=value".splitn(2, '=').next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Some("key=value".split_once('=').map_or("key=value", |x| x.0))` +LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` | = note: `-D clippy::manual-split-once` implied by `-D warnings` error: manual implementation of `split_once` --> $DIR/manual_split_once.rs:15:13 | -LL | let _ = "key=value".splitn(2, '=').next().unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').map_or("key=value", |x| x.0)` - -error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:16:13 - | -LL | let _ = "key=value".splitn(2, '=').nth(0).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').map_or("key=value", |x| x.0)` - -error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:17:13 - | -LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` - -error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:18:13 - | LL | let _ = "key=value".splitn(2, '=').skip(1).next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:19:18 + --> $DIR/manual_split_once.rs:16:18 | LL | let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=')` +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:19:13 + | +LL | let _ = s.splitn(2, '=').nth(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1` + error: manual implementation of `split_once` --> $DIR/manual_split_once.rs:22:13 | -LL | let _ = s.splitn(2, '=').next().unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(&*s, |x| x.0)` +LL | let _ = s.splitn(2, '=').nth(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` --> $DIR/manual_split_once.rs:25:13 | -LL | let _ = s.splitn(2, '=').nth(0).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(&*s, |x| x.0)` +LL | let _ = s.splitn(2, '=').skip(1).next().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:28:13 + --> $DIR/manual_split_once.rs:28:17 | -LL | let _ = s.splitn(2, '=').skip(0).next().unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(*s, |x| x.0)` +LL | let _ = s.splitn(2, '=').nth(1)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:31:17 + --> $DIR/manual_split_once.rs:29:17 | -LL | let _ = s.splitn(2, "key=value").next()?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value").map_or(s, |x| x.0)` - -error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:32:17 - | -LL | let _ = s.splitn(2, "key=value").nth(1)?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1` - -error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:33:17 - | -LL | let _ = s.splitn(2, "key=value").skip(1).next()?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1` +LL | let _ = s.splitn(2, '=').skip(1).next()?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1` error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:42:13 + --> $DIR/manual_split_once.rs:30:17 + | +LL | let _ = s.rsplitn(2, '=').nth(1)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0` + +error: manual implementation of `rsplit_once` + --> $DIR/manual_split_once.rs:31:17 + | +LL | let _ = s.rsplitn(2, '=').skip(1).next()?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0` + +error: manual implementation of `rsplit_once` + --> $DIR/manual_split_once.rs:39:13 | LL | let _ = "key=value".rsplitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').unwrap().0` error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:43:13 - | -LL | let _ = "key=value".rsplitn(2, '=').nth(0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|x| x.1)` - -error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:44:18 + --> $DIR/manual_split_once.rs:40:18 | LL | let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|(x, y)| (y, x))` +error: manual implementation of `rsplit_once` + --> $DIR/manual_split_once.rs:41:13 + | +LL | let _ = s.rsplitn(2, '=').nth(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=').map(|x| x.0)` + error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:55:13 + --> $DIR/manual_split_once.rs:45:5 + | +LL | let mut iter = "a.b.c".splitn(2, '.'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let l = iter.next().unwrap(); + | ----------------------------- first usage here +LL | let r = iter.next().unwrap(); + | ----------------------------- second usage here + | +help: try `split_once` + | +LL | let (l, r) = "a.b.c".split_once('.').unwrap(); + | +help: remove the `iter` usages + | +LL - let l = iter.next().unwrap(); +LL + + | +help: remove the `iter` usages + | +LL - let r = iter.next().unwrap(); +LL + + | + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:49:5 + | +LL | let mut iter = "a.b.c".splitn(2, '.'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let l = iter.next()?; + | --------------------- first usage here +LL | let r = iter.next()?; + | --------------------- second usage here + | +help: try `split_once` + | +LL | let (l, r) = "a.b.c".split_once('.')?; + | +help: remove the `iter` usages + | +LL - let l = iter.next()?; +LL + + | +help: remove the `iter` usages + | +LL - let r = iter.next()?; +LL + + | + +error: manual implementation of `rsplit_once` + --> $DIR/manual_split_once.rs:53:5 + | +LL | let mut iter = "a.b.c".rsplitn(2, '.'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let r = iter.next().unwrap(); + | ----------------------------- first usage here +LL | let l = iter.next().unwrap(); + | ----------------------------- second usage here + | +help: try `rsplit_once` + | +LL | let (l, r) = "a.b.c".rsplit_once('.').unwrap(); + | +help: remove the `iter` usages + | +LL - let r = iter.next().unwrap(); +LL + + | +help: remove the `iter` usages + | +LL - let l = iter.next().unwrap(); +LL + + | + +error: manual implementation of `rsplit_once` + --> $DIR/manual_split_once.rs:57:5 + | +LL | let mut iter = "a.b.c".rsplitn(2, '.'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let r = iter.next()?; + | --------------------- first usage here +LL | let l = iter.next()?; + | --------------------- second usage here + | +help: try `rsplit_once` + | +LL | let (l, r) = "a.b.c".rsplit_once('.')?; + | +help: remove the `iter` usages + | +LL - let r = iter.next()?; +LL + + | +help: remove the `iter` usages + | +LL - let l = iter.next()?; +LL + + | + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:142:13 | LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` -error: aborting due to 16 previous errors +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:144:5 + | +LL | let mut iter = "a.b.c".splitn(2, '.'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let a = iter.next().unwrap(); + | ----------------------------- first usage here +LL | let b = iter.next().unwrap(); + | ----------------------------- second usage here + | +help: try `split_once` + | +LL | let (a, b) = "a.b.c".split_once('.').unwrap(); + | +help: remove the `iter` usages + | +LL - let a = iter.next().unwrap(); +LL + + | +help: remove the `iter` usages + | +LL - let b = iter.next().unwrap(); +LL + + | + +error: aborting due to 19 previous errors diff --git a/tests/ui/mistyped_literal_suffix.fixed b/tests/ui/mistyped_literal_suffix.fixed index 5d57638af43..a7b36d53cd2 100644 --- a/tests/ui/mistyped_literal_suffix.fixed +++ b/tests/ui/mistyped_literal_suffix.fixed @@ -5,7 +5,8 @@ unused_variables, overflowing_literals, clippy::excessive_precision, - clippy::inconsistent_digit_grouping + clippy::inconsistent_digit_grouping, + clippy::unusual_byte_groupings )] fn main() { @@ -25,5 +26,18 @@ fn main() { let fail28 = 241_251_235E723_f64; let ok29 = 42279.911_32; + // testing that the suggestion actually fits in its type + let fail30 = 127_i8; // should be i8 + let fail31 = 240_u8; // should be u8 + let ok32 = 360_8; // doesnt fit in either, should be ignored + let fail33 = 0x1234_i16; + let fail34 = 0xABCD_u16; + let ok35 = 0x12345_16; + let fail36 = 0xFFFF_FFFF_FFFF_FFFF_u64; // u64 + + // issue #6129 + let ok37 = 123_32.123; + let ok38 = 124_64.0; + let _ = 1.123_45E1_f32; } diff --git a/tests/ui/mistyped_literal_suffix.rs b/tests/ui/mistyped_literal_suffix.rs index 12171452885..c97b31965c7 100644 --- a/tests/ui/mistyped_literal_suffix.rs +++ b/tests/ui/mistyped_literal_suffix.rs @@ -5,7 +5,8 @@ unused_variables, overflowing_literals, clippy::excessive_precision, - clippy::inconsistent_digit_grouping + clippy::inconsistent_digit_grouping, + clippy::unusual_byte_groupings )] fn main() { @@ -25,5 +26,18 @@ fn main() { let fail28 = 241251235E723_64; let ok29 = 42279.911_32; + // testing that the suggestion actually fits in its type + let fail30 = 127_8; // should be i8 + let fail31 = 240_8; // should be u8 + let ok32 = 360_8; // doesnt fit in either, should be ignored + let fail33 = 0x1234_16; + let fail34 = 0xABCD_16; + let ok35 = 0x12345_16; + let fail36 = 0xFFFF_FFFF_FFFF_FFFF_64; // u64 + + // issue #6129 + let ok37 = 123_32.123; + let ok38 = 124_64.0; + let _ = 1.12345E1_32; } diff --git a/tests/ui/mistyped_literal_suffix.stderr b/tests/ui/mistyped_literal_suffix.stderr index d24543c26e4..fb761d9bde4 100644 --- a/tests/ui/mistyped_literal_suffix.stderr +++ b/tests/ui/mistyped_literal_suffix.stderr @@ -1,5 +1,5 @@ error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:12:18 + --> $DIR/mistyped_literal_suffix.rs:13:18 | LL | let fail14 = 2_32; | ^^^^ help: did you mean to write: `2_i32` @@ -7,64 +7,94 @@ LL | let fail14 = 2_32; = note: `#[deny(clippy::mistyped_literal_suffixes)]` on by default error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:13:18 + --> $DIR/mistyped_literal_suffix.rs:14:18 | LL | let fail15 = 4_64; | ^^^^ help: did you mean to write: `4_i64` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:14:18 + --> $DIR/mistyped_literal_suffix.rs:15:18 | LL | let fail16 = 7_8; // | ^^^ help: did you mean to write: `7_i8` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:15:18 + --> $DIR/mistyped_literal_suffix.rs:16:18 | LL | let fail17 = 23_16; // | ^^^^^ help: did you mean to write: `23_i16` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:18:18 + --> $DIR/mistyped_literal_suffix.rs:19:18 | LL | let fail20 = 2__8; // | ^^^^ help: did you mean to write: `2_i8` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:19:18 + --> $DIR/mistyped_literal_suffix.rs:20:18 | LL | let fail21 = 4___16; // | ^^^^^^ help: did you mean to write: `4_i16` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:22:18 + --> $DIR/mistyped_literal_suffix.rs:23:18 | LL | let fail25 = 1E2_32; | ^^^^^^ help: did you mean to write: `1E2_f32` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:23:18 + --> $DIR/mistyped_literal_suffix.rs:24:18 | LL | let fail26 = 43E7_64; | ^^^^^^^ help: did you mean to write: `43E7_f64` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:24:18 + --> $DIR/mistyped_literal_suffix.rs:25:18 | LL | let fail27 = 243E17_32; | ^^^^^^^^^ help: did you mean to write: `243E17_f32` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:25:18 + --> $DIR/mistyped_literal_suffix.rs:26:18 | LL | let fail28 = 241251235E723_64; | ^^^^^^^^^^^^^^^^ help: did you mean to write: `241_251_235E723_f64` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:28:13 + --> $DIR/mistyped_literal_suffix.rs:30:18 + | +LL | let fail30 = 127_8; // should be i8 + | ^^^^^ help: did you mean to write: `127_i8` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:31:18 + | +LL | let fail31 = 240_8; // should be u8 + | ^^^^^ help: did you mean to write: `240_u8` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:33:18 + | +LL | let fail33 = 0x1234_16; + | ^^^^^^^^^ help: did you mean to write: `0x1234_i16` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:34:18 + | +LL | let fail34 = 0xABCD_16; + | ^^^^^^^^^ help: did you mean to write: `0xABCD_u16` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:36:18 + | +LL | let fail36 = 0xFFFF_FFFF_FFFF_FFFF_64; // u64 + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to write: `0xFFFF_FFFF_FFFF_FFFF_u64` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:42:13 | LL | let _ = 1.12345E1_32; | ^^^^^^^^^^^^ help: did you mean to write: `1.123_45E1_f32` -error: aborting due to 11 previous errors +error: aborting due to 16 previous errors diff --git a/tests/ui/mut_from_ref.rs b/tests/ui/mut_from_ref.rs index a9a04c8f56b..370dbd58821 100644 --- a/tests/ui/mut_from_ref.rs +++ b/tests/ui/mut_from_ref.rs @@ -5,7 +5,7 @@ struct Foo; impl Foo { fn this_wont_hurt_a_bit(&self) -> &mut Foo { - unimplemented!() + unsafe { unimplemented!() } } } @@ -15,29 +15,37 @@ trait Ouch { impl Ouch for Foo { fn ouch(x: &Foo) -> &mut Foo { - unimplemented!() + unsafe { unimplemented!() } } } fn fail(x: &u32) -> &mut u16 { - unimplemented!() + unsafe { unimplemented!() } } fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 { - unimplemented!() + unsafe { unimplemented!() } } fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 { - unimplemented!() + unsafe { unimplemented!() } } // this is OK, because the result borrows y fn works<'a>(x: &u32, y: &'a mut u32) -> &'a mut u32 { - unimplemented!() + unsafe { unimplemented!() } } // this is also OK, because the result could borrow y fn also_works<'a>(x: &'a u32, y: &'a mut u32) -> &'a mut u32 { + unsafe { unimplemented!() } +} + +unsafe fn also_broken(x: &u32) -> &mut u32 { + unimplemented!() +} + +fn without_unsafe(x: &u32) -> &mut u32 { unimplemented!() } diff --git a/tests/ui/mut_from_ref.stderr b/tests/ui/mut_from_ref.stderr index 4787999920b..b76d6a13ffb 100644 --- a/tests/ui/mut_from_ref.stderr +++ b/tests/ui/mut_from_ref.stderr @@ -59,5 +59,17 @@ note: immutable borrow here LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 { | ^^^^^^^ ^^^^^^^ -error: aborting due to 5 previous errors +error: mutable borrow from immutable input(s) + --> $DIR/mut_from_ref.rs:44:35 + | +LL | unsafe fn also_broken(x: &u32) -> &mut u32 { + | ^^^^^^^^ + | +note: immutable borrow here + --> $DIR/mut_from_ref.rs:44:26 + | +LL | unsafe fn also_broken(x: &u32) -> &mut u32 { + | ^^^^ + +error: aborting due to 6 previous errors diff --git a/tests/ui/needless_for_each_fixable.fixed b/tests/ui/needless_for_each_fixable.fixed index f00f9ee4c33..c1685f7b6d7 100644 --- a/tests/ui/needless_for_each_fixable.fixed +++ b/tests/ui/needless_for_each_fixable.fixed @@ -1,6 +1,11 @@ // run-rustfix #![warn(clippy::needless_for_each)] -#![allow(unused, clippy::needless_return, clippy::match_single_binding)] +#![allow( + unused, + clippy::needless_return, + clippy::match_single_binding, + clippy::let_unit_value +)] use std::collections::HashMap; diff --git a/tests/ui/needless_for_each_fixable.rs b/tests/ui/needless_for_each_fixable.rs index 1bd400d348b..ad17b0956fa 100644 --- a/tests/ui/needless_for_each_fixable.rs +++ b/tests/ui/needless_for_each_fixable.rs @@ -1,6 +1,11 @@ // run-rustfix #![warn(clippy::needless_for_each)] -#![allow(unused, clippy::needless_return, clippy::match_single_binding)] +#![allow( + unused, + clippy::needless_return, + clippy::match_single_binding, + clippy::let_unit_value +)] use std::collections::HashMap; diff --git a/tests/ui/needless_for_each_fixable.stderr b/tests/ui/needless_for_each_fixable.stderr index 6487e57266c..08e995851d7 100644 --- a/tests/ui/needless_for_each_fixable.stderr +++ b/tests/ui/needless_for_each_fixable.stderr @@ -1,5 +1,5 @@ error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:10:5 + --> $DIR/needless_for_each_fixable.rs:15:5 | LL | / v.iter().for_each(|elem| { LL | | acc += elem; @@ -15,7 +15,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:13:5 + --> $DIR/needless_for_each_fixable.rs:18:5 | LL | / v.into_iter().for_each(|elem| { LL | | acc += elem; @@ -30,7 +30,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:17:5 + --> $DIR/needless_for_each_fixable.rs:22:5 | LL | / [1, 2, 3].iter().for_each(|elem| { LL | | acc += elem; @@ -45,7 +45,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:22:5 + --> $DIR/needless_for_each_fixable.rs:27:5 | LL | / hash_map.iter().for_each(|(k, v)| { LL | | acc += k + v; @@ -60,7 +60,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:25:5 + --> $DIR/needless_for_each_fixable.rs:30:5 | LL | / hash_map.iter_mut().for_each(|(k, v)| { LL | | acc += *k + *v; @@ -75,7 +75,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:28:5 + --> $DIR/needless_for_each_fixable.rs:33:5 | LL | / hash_map.keys().for_each(|k| { LL | | acc += k; @@ -90,7 +90,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:31:5 + --> $DIR/needless_for_each_fixable.rs:36:5 | LL | / hash_map.values().for_each(|v| { LL | | acc += v; @@ -105,7 +105,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:38:5 + --> $DIR/needless_for_each_fixable.rs:43:5 | LL | / my_vec().iter().for_each(|elem| { LL | | acc += elem; diff --git a/tests/ui/needless_late_init.rs b/tests/ui/needless_late_init.rs index 89e012c066f..54e66b391b8 100644 --- a/tests/ui/needless_late_init.rs +++ b/tests/ui/needless_late_init.rs @@ -1,4 +1,15 @@ -#![allow(unused)] +#![feature(let_chains)] +#![allow(unused, clippy::nonminimal_bool, clippy::let_unit_value)] + +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; +use std::rc::Rc; + +struct SignificantDrop; +impl std::ops::Drop for SignificantDrop { + fn drop(&mut self) { + println!("dropped"); + } +} fn main() { let a; @@ -17,13 +28,6 @@ fn main() { b = "five" } - let c; - if let Some(n) = Some(5) { - c = n; - } else { - c = -50; - } - let d; if true { let temp = 5; @@ -36,7 +40,7 @@ fn main() { if true { e = format!("{} {}", a, b); } else { - e = format!("{}", c); + e = format!("{}", n); } let f; @@ -52,7 +56,27 @@ fn main() { panic!(); } - println!("{}", a); + // Drop order only matters if both are significant + let x; + let y = SignificantDrop; + x = 1; + + let x; + let y = 1; + x = SignificantDrop; + + let x; + // types that should be considered insignificant + let y = 1; + let y = "2"; + let y = String::new(); + let y = vec![3.0]; + let y = HashMap::::new(); + let y = BTreeMap::::new(); + let y = HashSet::::new(); + let y = BTreeSet::::new(); + let y = Box::new(4); + x = SignificantDrop; } async fn in_async() -> &'static str { @@ -176,5 +200,32 @@ fn does_not_lint() { } in_macro!(); - println!("{}", x); + // ignore if-lets - https://github.com/rust-lang/rust-clippy/issues/8613 + let x; + if let Some(n) = Some("v") { + x = 1; + } else { + x = 2; + } + + let x; + if true && let Some(n) = Some("let chains too") { + x = 1; + } else { + x = 2; + } + + // ignore mut bindings + // https://github.com/shepmaster/twox-hash/blob/b169c16d86eb8ea4a296b0acb9d00ca7e3c3005f/src/sixty_four.rs#L88-L93 + // https://github.com/dtolnay/thiserror/blob/21c26903e29cb92ba1a7ff11e82ae2001646b60d/tests/test_generics.rs#L91-L100 + let mut x: usize; + x = 1; + x = 2; + x = 3; + + // should not move the declaration if `x` has a significant drop, and there + // is another binding with a significant drop between it and the first usage + let x; + let y = SignificantDrop; + x = SignificantDrop; } diff --git a/tests/ui/needless_late_init.stderr b/tests/ui/needless_late_init.stderr index ef79e635d2a..d33a117b288 100644 --- a/tests/ui/needless_late_init.stderr +++ b/tests/ui/needless_late_init.stderr @@ -1,5 +1,5 @@ -error: unneeded late initalization - --> $DIR/needless_late_init.rs:4:5 +error: unneeded late initialization + --> $DIR/needless_late_init.rs:15:5 | LL | let a; | ^^^^^^ @@ -20,8 +20,8 @@ help: add a semicolon after the `match` expression LL | }; | + -error: unneeded late initalization - --> $DIR/needless_late_init.rs:13:5 +error: unneeded late initialization + --> $DIR/needless_late_init.rs:24:5 | LL | let b; | ^^^^^^ @@ -41,29 +41,8 @@ help: add a semicolon after the `if` expression LL | }; | + -error: unneeded late initalization - --> $DIR/needless_late_init.rs:20:5 - | -LL | let c; - | ^^^^^^ - | -help: declare `c` here - | -LL | let c = if let Some(n) = Some(5) { - | +++++++ -help: remove the assignments from the branches - | -LL ~ n -LL | } else { -LL ~ -50 - | -help: add a semicolon after the `if` expression - | -LL | }; - | + - -error: unneeded late initalization - --> $DIR/needless_late_init.rs:27:5 +error: unneeded late initialization + --> $DIR/needless_late_init.rs:31:5 | LL | let d; | ^^^^^^ @@ -83,8 +62,8 @@ help: add a semicolon after the `if` expression LL | }; | + -error: unneeded late initalization - --> $DIR/needless_late_init.rs:35:5 +error: unneeded late initialization + --> $DIR/needless_late_init.rs:39:5 | LL | let e; | ^^^^^^ @@ -97,15 +76,15 @@ help: remove the assignments from the branches | LL ~ format!("{} {}", a, b) LL | } else { -LL ~ format!("{}", c) +LL ~ format!("{}", n) | help: add a semicolon after the `if` expression | LL | }; | + -error: unneeded late initalization - --> $DIR/needless_late_init.rs:42:5 +error: unneeded late initialization + --> $DIR/needless_late_init.rs:46:5 | LL | let f; | ^^^^^^ @@ -120,8 +99,8 @@ LL - 1 => f = "three", LL + 1 => "three", | -error: unneeded late initalization - --> $DIR/needless_late_init.rs:48:5 +error: unneeded late initialization + --> $DIR/needless_late_init.rs:52:5 | LL | let g: usize; | ^^^^^^^^^^^^^ @@ -140,8 +119,50 @@ help: add a semicolon after the `if` expression LL | }; | + -error: unneeded late initalization - --> $DIR/needless_late_init.rs:63:5 +error: unneeded late initialization + --> $DIR/needless_late_init.rs:60:5 + | +LL | let x; + | ^^^^^^ created here +LL | let y = SignificantDrop; +LL | x = 1; + | ^^^^^ initialised here + | +help: declare `x` here + | +LL | let x = 1; + | ~~~~~ + +error: unneeded late initialization + --> $DIR/needless_late_init.rs:64:5 + | +LL | let x; + | ^^^^^^ created here +LL | let y = 1; +LL | x = SignificantDrop; + | ^^^^^^^^^^^^^^^^^^^ initialised here + | +help: declare `x` here + | +LL | let x = SignificantDrop; + | ~~~~~ + +error: unneeded late initialization + --> $DIR/needless_late_init.rs:68:5 + | +LL | let x; + | ^^^^^^ created here +... +LL | x = SignificantDrop; + | ^^^^^^^^^^^^^^^^^^^ initialised here + | +help: declare `x` here + | +LL | let x = SignificantDrop; + | ~~~~~ + +error: unneeded late initialization + --> $DIR/needless_late_init.rs:87:5 | LL | let a; | ^^^^^^ @@ -161,8 +182,8 @@ help: add a semicolon after the `match` expression LL | }; | + -error: unneeded late initalization - --> $DIR/needless_late_init.rs:80:5 +error: unneeded late initialization + --> $DIR/needless_late_init.rs:104:5 | LL | let a; | ^^^^^^ @@ -182,5 +203,5 @@ help: add a semicolon after the `match` expression LL | }; | + -error: aborting due to 9 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/needless_late_init_fixable.fixed b/tests/ui/needless_late_init_fixable.fixed index b516f9d86b7..724477e8691 100644 --- a/tests/ui/needless_late_init_fixable.fixed +++ b/tests/ui/needless_late_init_fixable.fixed @@ -15,11 +15,5 @@ fn main() { let d: usize = 1; - let mut e = 1; - e = 2; - - - let h = format!("{}", e); - - println!("{}", a); + let e = format!("{}", d); } diff --git a/tests/ui/needless_late_init_fixable.rs b/tests/ui/needless_late_init_fixable.rs index 75a4bc916de..3e6bd363672 100644 --- a/tests/ui/needless_late_init_fixable.rs +++ b/tests/ui/needless_late_init_fixable.rs @@ -14,12 +14,6 @@ fn main() { let d: usize; d = 1; - let mut e; - e = 1; - e = 2; - - let h; - h = format!("{}", e); - - println!("{}", a); + let e; + e = format!("{}", d); } diff --git a/tests/ui/needless_late_init_fixable.stderr b/tests/ui/needless_late_init_fixable.stderr index 3f3d4f5286b..8c664309e3e 100644 --- a/tests/ui/needless_late_init_fixable.stderr +++ b/tests/ui/needless_late_init_fixable.stderr @@ -1,8 +1,10 @@ -error: unneeded late initalization +error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:6:5 | LL | let a; - | ^^^^^^ + | ^^^^^^ created here +LL | a = "zero"; + | ^^^^^^^^^^ initialised here | = note: `-D clippy::needless-late-init` implied by `-D warnings` help: declare `a` here @@ -10,60 +12,59 @@ help: declare `a` here LL | let a = "zero"; | ~~~~~ -error: unneeded late initalization +error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:9:5 | LL | let b; - | ^^^^^^ + | ^^^^^^ created here +LL | let c; +LL | b = 1; + | ^^^^^ initialised here | help: declare `b` here | LL | let b = 1; | ~~~~~ -error: unneeded late initalization +error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:10:5 | LL | let c; - | ^^^^^^ + | ^^^^^^ created here +LL | b = 1; +LL | c = 2; + | ^^^^^ initialised here | help: declare `c` here | LL | let c = 2; | ~~~~~ -error: unneeded late initalization +error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:14:5 | LL | let d: usize; - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ created here +LL | d = 1; + | ^^^^^ initialised here | help: declare `d` here | LL | let d: usize = 1; | ~~~~~~~~~~~~ -error: unneeded late initalization +error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:17:5 | -LL | let mut e; - | ^^^^^^^^^^ +LL | let e; + | ^^^^^^ created here +LL | e = format!("{}", d); + | ^^^^^^^^^^^^^^^^^^^^ initialised here | help: declare `e` here | -LL | let mut e = 1; - | ~~~~~~~~~ - -error: unneeded late initalization - --> $DIR/needless_late_init_fixable.rs:21:5 - | -LL | let h; - | ^^^^^^ - | -help: declare `h` here - | -LL | let h = format!("{}", e); +LL | let e = format!("{}", d); | ~~~~~ -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/needless_match.fixed b/tests/ui/needless_match.fixed index 9ccccaa1725..b997e5316cf 100644 --- a/tests/ui/needless_match.fixed +++ b/tests/ui/needless_match.fixed @@ -80,6 +80,18 @@ fn if_let_option() { } else { None }; + + // Don't trigger + let _ = if let Some(a) = Some(1) { Some(a) } else { Some(2) }; +} + +fn if_let_option_result() -> Result<(), ()> { + fn f(x: i32) -> Result, ()> { + Ok(Some(x)) + } + // Don't trigger + let _ = if let Some(v) = f(1)? { Some(v) } else { f(2)? }; + Ok(()) } fn if_let_result() { diff --git a/tests/ui/needless_match.rs b/tests/ui/needless_match.rs index d210ecff7f1..90482775a1e 100644 --- a/tests/ui/needless_match.rs +++ b/tests/ui/needless_match.rs @@ -103,6 +103,18 @@ fn if_let_option() { } else { None }; + + // Don't trigger + let _ = if let Some(a) = Some(1) { Some(a) } else { Some(2) }; +} + +fn if_let_option_result() -> Result<(), ()> { + fn f(x: i32) -> Result, ()> { + Ok(Some(x)) + } + // Don't trigger + let _ = if let Some(v) = f(1)? { Some(v) } else { f(2)? }; + Ok(()) } fn if_let_result() { diff --git a/tests/ui/needless_match.stderr b/tests/ui/needless_match.stderr index 34c5226f060..2d679631c6f 100644 --- a/tests/ui/needless_match.stderr +++ b/tests/ui/needless_match.stderr @@ -72,19 +72,19 @@ LL | let _ = if let Some(a) = Some(1) { Some(a) } else { None }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `Some(1)` error: this if-let expression is unnecessary - --> $DIR/needless_match.rs:110:31 + --> $DIR/needless_match.rs:122:31 | LL | let _: Result = if let Err(e) = x { Err(e) } else { x }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x` error: this if-let expression is unnecessary - --> $DIR/needless_match.rs:111:31 + --> $DIR/needless_match.rs:123:31 | LL | let _: Result = if let Ok(val) = x { Ok(val) } else { x }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x` error: this if-let expression is unnecessary - --> $DIR/needless_match.rs:117:21 + --> $DIR/needless_match.rs:129:21 | LL | let _: Simple = if let Simple::A = x { | _____________________^ @@ -97,7 +97,7 @@ LL | | }; | |_____^ help: replace it with: `x` error: this match expression is unnecessary - --> $DIR/needless_match.rs:156:26 + --> $DIR/needless_match.rs:168:26 | LL | let _: Complex = match ce { | __________________________^ diff --git a/tests/ui/needless_option_as_deref.fixed b/tests/ui/needless_option_as_deref.fixed index c09b07db3dc..acd22c6bb43 100644 --- a/tests/ui/needless_option_as_deref.fixed +++ b/tests/ui/needless_option_as_deref.fixed @@ -16,6 +16,20 @@ fn main() { let _ = Some(Box::new(1)).as_deref(); let _ = Some(Box::new(1)).as_deref_mut(); + let mut y = 0; + let mut x = Some(&mut y); + for _ in 0..3 { + let _ = x.as_deref_mut(); + } + + let mut y = 0; + let mut x = Some(&mut y); + let mut closure = || { + let _ = x.as_deref_mut(); + }; + closure(); + closure(); + // #7846 let mut i = 0; let mut opt_vec = vec![Some(&mut i)]; diff --git a/tests/ui/needless_option_as_deref.rs b/tests/ui/needless_option_as_deref.rs index c3ba27ecccf..61eda5052a2 100644 --- a/tests/ui/needless_option_as_deref.rs +++ b/tests/ui/needless_option_as_deref.rs @@ -16,6 +16,20 @@ fn main() { let _ = Some(Box::new(1)).as_deref(); let _ = Some(Box::new(1)).as_deref_mut(); + let mut y = 0; + let mut x = Some(&mut y); + for _ in 0..3 { + let _ = x.as_deref_mut(); + } + + let mut y = 0; + let mut x = Some(&mut y); + let mut closure = || { + let _ = x.as_deref_mut(); + }; + closure(); + closure(); + // #7846 let mut i = 0; let mut opt_vec = vec![Some(&mut i)]; diff --git a/tests/ui/needless_option_take.fixed b/tests/ui/needless_option_take.fixed new file mode 100644 index 00000000000..29691e81666 --- /dev/null +++ b/tests/ui/needless_option_take.fixed @@ -0,0 +1,15 @@ +// run-rustfix + +fn main() { + println!("Testing non erroneous option_take_on_temporary"); + let mut option = Some(1); + let _ = Box::new(move || option.take().unwrap()); + + println!("Testing non erroneous option_take_on_temporary"); + let x = Some(3); + x.as_ref(); + + println!("Testing erroneous option_take_on_temporary"); + let x = Some(3); + x.as_ref(); +} diff --git a/tests/ui/needless_option_take.rs b/tests/ui/needless_option_take.rs new file mode 100644 index 00000000000..9f4109eb463 --- /dev/null +++ b/tests/ui/needless_option_take.rs @@ -0,0 +1,15 @@ +// run-rustfix + +fn main() { + println!("Testing non erroneous option_take_on_temporary"); + let mut option = Some(1); + let _ = Box::new(move || option.take().unwrap()); + + println!("Testing non erroneous option_take_on_temporary"); + let x = Some(3); + x.as_ref(); + + println!("Testing erroneous option_take_on_temporary"); + let x = Some(3); + x.as_ref().take(); +} diff --git a/tests/ui/needless_option_take.stderr b/tests/ui/needless_option_take.stderr new file mode 100644 index 00000000000..cb3bf015b36 --- /dev/null +++ b/tests/ui/needless_option_take.stderr @@ -0,0 +1,10 @@ +error: called `Option::take()` on a temporary value + --> $DIR/needless_option_take.rs:14:5 + | +LL | x.as_ref().take(); + | ^^^^^^^^^^^^^^^^^ help: try: `x.as_ref()` + | + = note: `-D clippy::needless-option-take` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/needless_splitn.fixed b/tests/ui/needless_splitn.fixed index f6a4b2f17d3..61f5fc4e679 100644 --- a/tests/ui/needless_splitn.fixed +++ b/tests/ui/needless_splitn.fixed @@ -24,4 +24,24 @@ fn main() { let _ = str.rsplitn(2, '=').nth(1); let (_, _) = str.rsplitn(2, '=').next_tuple().unwrap(); let (_, _) = str.rsplit('=').next_tuple().unwrap(); + + let _ = str.split('=').next(); + let _ = str.split('=').nth(3); + let _ = str.splitn(5, '=').nth(4); + let _ = str.splitn(5, '=').nth(5); +} + +fn _question_mark(s: &str) -> Option<()> { + let _ = s.split('=').next()?; + let _ = s.split('=').nth(0)?; + let _ = s.rsplit('=').next()?; + let _ = s.rsplit('=').nth(0)?; + + Some(()) +} + +fn _test_msrv() { + #![clippy::msrv = "1.51"] + // `manual_split_once` MSRV shouldn't apply to `needless_splitn` + let _ = "key=value".split('=').nth(0).unwrap(); } diff --git a/tests/ui/needless_splitn.rs b/tests/ui/needless_splitn.rs index 6ba32255bb2..71d9a7077fa 100644 --- a/tests/ui/needless_splitn.rs +++ b/tests/ui/needless_splitn.rs @@ -24,4 +24,24 @@ fn main() { let _ = str.rsplitn(2, '=').nth(1); let (_, _) = str.rsplitn(2, '=').next_tuple().unwrap(); let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap(); + + let _ = str.splitn(5, '=').next(); + let _ = str.splitn(5, '=').nth(3); + let _ = str.splitn(5, '=').nth(4); + let _ = str.splitn(5, '=').nth(5); +} + +fn _question_mark(s: &str) -> Option<()> { + let _ = s.splitn(2, '=').next()?; + let _ = s.splitn(2, '=').nth(0)?; + let _ = s.rsplitn(2, '=').next()?; + let _ = s.rsplitn(2, '=').nth(0)?; + + Some(()) +} + +fn _test_msrv() { + #![clippy::msrv = "1.51"] + // `manual_split_once` MSRV shouldn't apply to `needless_splitn` + let _ = "key=value".splitn(2, '=').nth(0).unwrap(); } diff --git a/tests/ui/needless_splitn.stderr b/tests/ui/needless_splitn.stderr index 66de2256554..f112b29e7f2 100644 --- a/tests/ui/needless_splitn.stderr +++ b/tests/ui/needless_splitn.stderr @@ -36,5 +36,47 @@ error: unnecessary use of `rsplitn` LL | let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap(); | ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')` -error: aborting due to 6 previous errors +error: unnecessary use of `splitn` + --> $DIR/needless_splitn.rs:28:13 + | +LL | let _ = str.splitn(5, '=').next(); + | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')` + +error: unnecessary use of `splitn` + --> $DIR/needless_splitn.rs:29:13 + | +LL | let _ = str.splitn(5, '=').nth(3); + | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')` + +error: unnecessary use of `splitn` + --> $DIR/needless_splitn.rs:35:13 + | +LL | let _ = s.splitn(2, '=').next()?; + | ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')` + +error: unnecessary use of `splitn` + --> $DIR/needless_splitn.rs:36:13 + | +LL | let _ = s.splitn(2, '=').nth(0)?; + | ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')` + +error: unnecessary use of `rsplitn` + --> $DIR/needless_splitn.rs:37:13 + | +LL | let _ = s.rsplitn(2, '=').next()?; + | ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')` + +error: unnecessary use of `rsplitn` + --> $DIR/needless_splitn.rs:38:13 + | +LL | let _ = s.rsplitn(2, '=').nth(0)?; + | ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')` + +error: unnecessary use of `splitn` + --> $DIR/needless_splitn.rs:46:13 + | +LL | let _ = "key=value".splitn(2, '=').nth(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split('=')` + +error: aborting due to 13 previous errors diff --git a/tests/ui/new_without_default.rs b/tests/ui/new_without_default.rs index e94f99c95f4..538927b1805 100644 --- a/tests/ui/new_without_default.rs +++ b/tests/ui/new_without_default.rs @@ -1,4 +1,4 @@ -#![allow(dead_code, clippy::missing_safety_doc)] +#![allow(dead_code, clippy::missing_safety_doc, clippy::extra_unused_lifetimes)] #![warn(clippy::new_without_default)] pub struct Foo; diff --git a/tests/ui/non_expressive_names.rs b/tests/ui/non_expressive_names.rs index 9937005d68d..583096ac054 100644 --- a/tests/ui/non_expressive_names.rs +++ b/tests/ui/non_expressive_names.rs @@ -1,5 +1,5 @@ #![warn(clippy::all)] -#![allow(unused, clippy::println_empty_string, non_snake_case)] +#![allow(unused, clippy::println_empty_string, non_snake_case, clippy::let_unit_value)] #[derive(Clone, Debug)] enum MaybeInst { diff --git a/tests/ui/numbered_fields.fixed b/tests/ui/numbered_fields.fixed index 1da97e96879..3710b3e9c81 100644 --- a/tests/ui/numbered_fields.fixed +++ b/tests/ui/numbered_fields.fixed @@ -30,4 +30,9 @@ fn main() { // Ok because it's in macro let _ = tuple_struct_init!(); + + type Alias = TupleStruct; + + // Aliases can't be tuple constructed #8638 + let _ = Alias { 0: 0, 1: 1, 2: 2 }; } diff --git a/tests/ui/numbered_fields.rs b/tests/ui/numbered_fields.rs index 08ec405a560..2af84bc0642 100644 --- a/tests/ui/numbered_fields.rs +++ b/tests/ui/numbered_fields.rs @@ -38,4 +38,9 @@ fn main() { // Ok because it's in macro let _ = tuple_struct_init!(); + + type Alias = TupleStruct; + + // Aliases can't be tuple constructed #8638 + let _ = Alias { 0: 0, 1: 1, 2: 2 }; } diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index 7790c816481..e12e13a57f1 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -1,6 +1,11 @@ // run-rustfix #![warn(clippy::option_if_let_else)] -#![allow(clippy::redundant_closure, clippy::ref_option_ref, clippy::equatable_if_let)] +#![allow( + clippy::redundant_closure, + clippy::ref_option_ref, + clippy::equatable_if_let, + clippy::let_unit_value +)] fn bad1(string: Option<&str>) -> (bool, &str) { string.map_or((false, "hello"), |x| (true, x)) diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index 3d9f76ee4a6..b5206fc26a9 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -1,6 +1,11 @@ // run-rustfix #![warn(clippy::option_if_let_else)] -#![allow(clippy::redundant_closure, clippy::ref_option_ref, clippy::equatable_if_let)] +#![allow( + clippy::redundant_closure, + clippy::ref_option_ref, + clippy::equatable_if_let, + clippy::let_unit_value +)] fn bad1(string: Option<&str>) -> (bool, &str) { if let Some(x) = string { diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index 546131ceb5b..40aef977b98 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -1,5 +1,5 @@ error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:6:5 + --> $DIR/option_if_let_else.rs:11:5 | LL | / if let Some(x) = string { LL | | (true, x) @@ -11,19 +11,19 @@ LL | | } = note: `-D clippy::option-if-let-else` implied by `-D warnings` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:24:13 + --> $DIR/option_if_let_else.rs:29:13 | LL | let _ = if let Some(s) = *string { s.len() } else { 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.map_or(0, |s| s.len())` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:25:13 + --> $DIR/option_if_let_else.rs:30:13 | LL | let _ = if let Some(s) = &num { s } else { &0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:26:13 + --> $DIR/option_if_let_else.rs:31:13 | LL | let _ = if let Some(s) = &mut num { | _____________^ @@ -43,13 +43,13 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:32:13 + --> $DIR/option_if_let_else.rs:37:13 | LL | let _ = if let Some(ref s) = num { s } else { &0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:33:13 + --> $DIR/option_if_let_else.rs:38:13 | LL | let _ = if let Some(mut s) = num { | _____________^ @@ -69,7 +69,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:39:13 + --> $DIR/option_if_let_else.rs:44:13 | LL | let _ = if let Some(ref mut s) = num { | _____________^ @@ -89,7 +89,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:48:5 + --> $DIR/option_if_let_else.rs:53:5 | LL | / if let Some(x) = arg { LL | | let y = x * x; @@ -108,7 +108,7 @@ LL + }) | error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:61:13 + --> $DIR/option_if_let_else.rs:66:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -120,7 +120,7 @@ LL | | }; | |_____^ help: try: `arg.map_or_else(|| side_effect(), |x| x)` error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:70:13 + --> $DIR/option_if_let_else.rs:75:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -143,7 +143,7 @@ LL ~ }, |x| x * x * x * x); | error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:103:13 + --> $DIR/option_if_let_else.rs:108:13 | LL | / if let Some(idx) = s.find('.') { LL | | vec![s[..idx].to_string(), s[idx..].to_string()] @@ -153,13 +153,13 @@ LL | | } | |_____________^ help: try: `s.find('.').map_or_else(|| vec![s.to_string()], |idx| vec![s[..idx].to_string(), s[idx..].to_string()])` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:127:13 + --> $DIR/option_if_let_else.rs:132:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:136:13 + --> $DIR/option_if_let_else.rs:141:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -181,13 +181,13 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:164:13 + --> $DIR/option_if_let_else.rs:169:13 | LL | let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or(s.len(), |x| s.len() + x)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:168:13 + --> $DIR/option_if_let_else.rs:173:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ diff --git a/tests/ui/option_take_on_temporary.fixed b/tests/ui/option_take_on_temporary.fixed new file mode 100644 index 00000000000..29691e81666 --- /dev/null +++ b/tests/ui/option_take_on_temporary.fixed @@ -0,0 +1,15 @@ +// run-rustfix + +fn main() { + println!("Testing non erroneous option_take_on_temporary"); + let mut option = Some(1); + let _ = Box::new(move || option.take().unwrap()); + + println!("Testing non erroneous option_take_on_temporary"); + let x = Some(3); + x.as_ref(); + + println!("Testing erroneous option_take_on_temporary"); + let x = Some(3); + x.as_ref(); +} diff --git a/tests/ui/or_then_unwrap.fixed b/tests/ui/or_then_unwrap.fixed index 6e0d5a87f68..844cc4b7a09 100644 --- a/tests/ui/or_then_unwrap.fixed +++ b/tests/ui/or_then_unwrap.fixed @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::or_then_unwrap)] -#![allow(clippy::map_identity)] +#![allow(clippy::map_identity, clippy::let_unit_value)] struct SomeStruct; impl SomeStruct { diff --git a/tests/ui/or_then_unwrap.rs b/tests/ui/or_then_unwrap.rs index e406a71d46d..1528ef9be96 100644 --- a/tests/ui/or_then_unwrap.rs +++ b/tests/ui/or_then_unwrap.rs @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::or_then_unwrap)] -#![allow(clippy::map_identity)] +#![allow(clippy::map_identity, clippy::let_unit_value)] struct SomeStruct; impl SomeStruct { diff --git a/tests/ui/panicking_macros.rs b/tests/ui/panicking_macros.rs index 12a0c776ae2..041ef17fa68 100644 --- a/tests/ui/panicking_macros.rs +++ b/tests/ui/panicking_macros.rs @@ -1,4 +1,4 @@ -#![allow(clippy::assertions_on_constants, clippy::eq_op)] +#![allow(clippy::assertions_on_constants, clippy::eq_op, clippy::let_unit_value)] #![feature(inline_const)] #![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)] diff --git a/tests/ui/pub_use.rs b/tests/ui/pub_use.rs new file mode 100644 index 00000000000..65542bedec7 --- /dev/null +++ b/tests/ui/pub_use.rs @@ -0,0 +1,14 @@ +#![warn(clippy::pub_use)] +#![allow(unused_imports)] +#![no_main] + +pub mod outer { + mod inner { + pub struct Test {} + } + // should be linted + pub use inner::Test; +} + +// should not be linted +use std::fmt; diff --git a/tests/ui/pub_use.stderr b/tests/ui/pub_use.stderr new file mode 100644 index 00000000000..9ab710df818 --- /dev/null +++ b/tests/ui/pub_use.stderr @@ -0,0 +1,11 @@ +error: using `pub use` + --> $DIR/pub_use.rs:10:5 + | +LL | pub use inner::Test; + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::pub-use` implied by `-D warnings` + = help: move the exported item to a public module instead + +error: aborting due to previous error + diff --git a/tests/ui/redundant_pub_crate.fixed b/tests/ui/redundant_pub_crate.fixed index 25f2fd061b8..106947de68c 100644 --- a/tests/ui/redundant_pub_crate.fixed +++ b/tests/ui/redundant_pub_crate.fixed @@ -104,4 +104,14 @@ mod m4 { pub use m4::*; +mod issue_8732 { + #[allow(unused_macros)] + macro_rules! some_macro { + () => {}; + } + + #[allow(unused_imports)] + pub(crate) use some_macro; // ok: macro exports are exempt +} + fn main() {} diff --git a/tests/ui/redundant_pub_crate.rs b/tests/ui/redundant_pub_crate.rs index 616286b4f39..f96cfd31843 100644 --- a/tests/ui/redundant_pub_crate.rs +++ b/tests/ui/redundant_pub_crate.rs @@ -104,4 +104,14 @@ mod m4 { pub use m4::*; +mod issue_8732 { + #[allow(unused_macros)] + macro_rules! some_macro { + () => {}; + } + + #[allow(unused_imports)] + pub(crate) use some_macro; // ok: macro exports are exempt +} + fn main() {} diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index 24a0c812291..9c4079ad6d3 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -1,71 +1,70 @@ -//! Test for Clippy lint renames. +// This file was generated by `cargo dev update_lints`. +// Use that command to update this file and do not edit by hand. +// Manual edits will be overwritten. + // run-rustfix -#![allow(dead_code)] -// allow the new lint name here, to test if the new name works -#![allow(clippy::module_name_repetitions)] -#![allow(clippy::new_without_default)] +#![allow(clippy::blocks_in_if_conditions)] +#![allow(clippy::box_collection)] #![allow(clippy::redundant_static_lifetimes)] #![allow(clippy::cognitive_complexity)] -#![allow(clippy::bind_instead_of_map)] -#![allow(clippy::box_collection)] -#![allow(clippy::blocks_in_if_conditions)] -#![allow(clippy::map_unwrap_or)] -#![allow(clippy::unwrap_used)] -#![allow(clippy::expect_used)] +#![allow(clippy::disallowed_methods)] +#![allow(clippy::disallowed_types)] #![allow(clippy::for_loops_over_fallibles)] #![allow(clippy::useless_conversion)] -#![allow(clippy::invisible_characters)] -#![allow(clippy::single_char_add_str)] #![allow(clippy::match_result_ok)] -#![allow(clippy::disallowed_types)] -#![allow(clippy::disallowed_methods)] +#![allow(clippy::new_without_default)] +#![allow(clippy::bind_instead_of_map)] +#![allow(clippy::expect_used)] +#![allow(clippy::map_unwrap_or)] +#![allow(clippy::unwrap_used)] +#![allow(clippy::needless_borrow)] +#![allow(clippy::single_char_add_str)] +#![allow(clippy::module_name_repetitions)] #![allow(clippy::recursive_format_impl)] -// uplifted lints -#![allow(invalid_value)] -#![allow(array_into_iter)] -#![allow(unused_labels)] +#![allow(clippy::invisible_characters)] #![allow(drop_bounds)] -#![allow(temporary_cstring_as_ptr)] -#![allow(non_fmt_panics)] -#![allow(unknown_lints)] +#![allow(array_into_iter)] #![allow(invalid_atomic_ordering)] +#![allow(invalid_value)] #![allow(enum_intrinsics_non_enums)] -// warn for the old lint name here, to test if the renaming worked -#![warn(clippy::module_name_repetitions)] -#![warn(clippy::new_without_default)] +#![allow(non_fmt_panics)] +#![allow(temporary_cstring_as_ptr)] +#![allow(unknown_lints)] +#![allow(unused_labels)] +#![warn(clippy::blocks_in_if_conditions)] +#![warn(clippy::blocks_in_if_conditions)] +#![warn(clippy::box_collection)] #![warn(clippy::redundant_static_lifetimes)] #![warn(clippy::cognitive_complexity)] -#![warn(clippy::bind_instead_of_map)] -#![warn(clippy::box_collection)] -#![warn(clippy::blocks_in_if_conditions)] -#![warn(clippy::blocks_in_if_conditions)] -#![warn(clippy::map_unwrap_or)] -#![warn(clippy::map_unwrap_or)] -#![warn(clippy::map_unwrap_or)] -#![warn(clippy::unwrap_used)] -#![warn(clippy::unwrap_used)] -#![warn(clippy::expect_used)] -#![warn(clippy::expect_used)] +#![warn(clippy::disallowed_methods)] +#![warn(clippy::disallowed_types)] #![warn(clippy::for_loops_over_fallibles)] #![warn(clippy::for_loops_over_fallibles)] #![warn(clippy::useless_conversion)] -#![warn(clippy::invisible_characters)] -#![warn(clippy::single_char_add_str)] #![warn(clippy::match_result_ok)] -#![warn(clippy::disallowed_types)] -#![warn(clippy::disallowed_methods)] +#![warn(clippy::new_without_default)] +#![warn(clippy::bind_instead_of_map)] +#![warn(clippy::expect_used)] +#![warn(clippy::map_unwrap_or)] +#![warn(clippy::map_unwrap_or)] +#![warn(clippy::unwrap_used)] #![warn(clippy::needless_borrow)] +#![warn(clippy::expect_used)] +#![warn(clippy::map_unwrap_or)] +#![warn(clippy::unwrap_used)] +#![warn(clippy::single_char_add_str)] +#![warn(clippy::module_name_repetitions)] #![warn(clippy::recursive_format_impl)] -// uplifted lints -#![warn(invalid_value)] -#![warn(array_into_iter)] -#![warn(unused_labels)] +#![warn(clippy::invisible_characters)] #![warn(drop_bounds)] -#![warn(temporary_cstring_as_ptr)] -#![warn(non_fmt_panics)] -#![warn(unknown_lints)] +#![warn(array_into_iter)] #![warn(invalid_atomic_ordering)] +#![warn(invalid_value)] #![warn(enum_intrinsics_non_enums)] +#![warn(non_fmt_panics)] +#![warn(temporary_cstring_as_ptr)] +#![warn(unknown_lints)] +#![warn(unused_labels)] fn main() {} diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index ea64234c680..e83e66b7fbd 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -1,71 +1,70 @@ -//! Test for Clippy lint renames. +// This file was generated by `cargo dev update_lints`. +// Use that command to update this file and do not edit by hand. +// Manual edits will be overwritten. + // run-rustfix -#![allow(dead_code)] -// allow the new lint name here, to test if the new name works -#![allow(clippy::module_name_repetitions)] -#![allow(clippy::new_without_default)] +#![allow(clippy::blocks_in_if_conditions)] +#![allow(clippy::box_collection)] #![allow(clippy::redundant_static_lifetimes)] #![allow(clippy::cognitive_complexity)] -#![allow(clippy::bind_instead_of_map)] -#![allow(clippy::box_collection)] -#![allow(clippy::blocks_in_if_conditions)] -#![allow(clippy::map_unwrap_or)] -#![allow(clippy::unwrap_used)] -#![allow(clippy::expect_used)] +#![allow(clippy::disallowed_methods)] +#![allow(clippy::disallowed_types)] #![allow(clippy::for_loops_over_fallibles)] #![allow(clippy::useless_conversion)] -#![allow(clippy::invisible_characters)] -#![allow(clippy::single_char_add_str)] #![allow(clippy::match_result_ok)] -#![allow(clippy::disallowed_types)] -#![allow(clippy::disallowed_methods)] +#![allow(clippy::new_without_default)] +#![allow(clippy::bind_instead_of_map)] +#![allow(clippy::expect_used)] +#![allow(clippy::map_unwrap_or)] +#![allow(clippy::unwrap_used)] +#![allow(clippy::needless_borrow)] +#![allow(clippy::single_char_add_str)] +#![allow(clippy::module_name_repetitions)] #![allow(clippy::recursive_format_impl)] -// uplifted lints -#![allow(invalid_value)] -#![allow(array_into_iter)] -#![allow(unused_labels)] +#![allow(clippy::invisible_characters)] #![allow(drop_bounds)] -#![allow(temporary_cstring_as_ptr)] -#![allow(non_fmt_panics)] -#![allow(unknown_lints)] +#![allow(array_into_iter)] #![allow(invalid_atomic_ordering)] +#![allow(invalid_value)] #![allow(enum_intrinsics_non_enums)] -// warn for the old lint name here, to test if the renaming worked -#![warn(clippy::stutter)] -#![warn(clippy::new_without_default_derive)] -#![warn(clippy::const_static_lifetime)] -#![warn(clippy::cyclomatic_complexity)] -#![warn(clippy::option_and_then_some)] -#![warn(clippy::box_vec)] +#![allow(non_fmt_panics)] +#![allow(temporary_cstring_as_ptr)] +#![allow(unknown_lints)] +#![allow(unused_labels)] #![warn(clippy::block_in_if_condition_expr)] #![warn(clippy::block_in_if_condition_stmt)] -#![warn(clippy::option_map_unwrap_or)] -#![warn(clippy::option_map_unwrap_or_else)] -#![warn(clippy::result_map_unwrap_or_else)] -#![warn(clippy::option_unwrap_used)] -#![warn(clippy::result_unwrap_used)] -#![warn(clippy::option_expect_used)] -#![warn(clippy::result_expect_used)] +#![warn(clippy::box_vec)] +#![warn(clippy::const_static_lifetime)] +#![warn(clippy::cyclomatic_complexity)] +#![warn(clippy::disallowed_method)] +#![warn(clippy::disallowed_type)] #![warn(clippy::for_loop_over_option)] #![warn(clippy::for_loop_over_result)] #![warn(clippy::identity_conversion)] -#![warn(clippy::zero_width_space)] -#![warn(clippy::single_char_push_str)] #![warn(clippy::if_let_some_result)] -#![warn(clippy::disallowed_type)] -#![warn(clippy::disallowed_method)] +#![warn(clippy::new_without_default_derive)] +#![warn(clippy::option_and_then_some)] +#![warn(clippy::option_expect_used)] +#![warn(clippy::option_map_unwrap_or)] +#![warn(clippy::option_map_unwrap_or_else)] +#![warn(clippy::option_unwrap_used)] #![warn(clippy::ref_in_deref)] +#![warn(clippy::result_expect_used)] +#![warn(clippy::result_map_unwrap_or_else)] +#![warn(clippy::result_unwrap_used)] +#![warn(clippy::single_char_push_str)] +#![warn(clippy::stutter)] #![warn(clippy::to_string_in_display)] -// uplifted lints -#![warn(clippy::invalid_ref)] -#![warn(clippy::into_iter_on_array)] -#![warn(clippy::unused_label)] +#![warn(clippy::zero_width_space)] #![warn(clippy::drop_bounds)] -#![warn(clippy::temporary_cstring_as_ptr)] -#![warn(clippy::panic_params)] -#![warn(clippy::unknown_clippy_lints)] +#![warn(clippy::into_iter_on_array)] #![warn(clippy::invalid_atomic_ordering)] +#![warn(clippy::invalid_ref)] #![warn(clippy::mem_discriminant_non_enum)] +#![warn(clippy::panic_params)] +#![warn(clippy::temporary_cstring_as_ptr)] +#![warn(clippy::unknown_clippy_lints)] +#![warn(clippy::unused_label)] fn main() {} diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index 8b132a78384..f811b10d017 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -1,208 +1,208 @@ -error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` +error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions` --> $DIR/rename.rs:35:9 | -LL | #![warn(clippy::stutter)] - | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` +LL | #![warn(clippy::block_in_if_condition_expr)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` | = note: `-D renamed-and-removed-lints` implied by `-D warnings` -error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` +error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions` --> $DIR/rename.rs:36:9 | -LL | #![warn(clippy::new_without_default_derive)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` +LL | #![warn(clippy::block_in_if_condition_stmt)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` + +error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` + --> $DIR/rename.rs:37:9 + | +LL | #![warn(clippy::box_vec)] + | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> $DIR/rename.rs:37:9 + --> $DIR/rename.rs:38:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> $DIR/rename.rs:38:9 + --> $DIR/rename.rs:39:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` +error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` + --> $DIR/rename.rs:40:9 + | +LL | #![warn(clippy::disallowed_method)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` + +error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` + --> $DIR/rename.rs:41:9 + | +LL | #![warn(clippy::disallowed_type)] + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` + +error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles` + --> $DIR/rename.rs:42:9 + | +LL | #![warn(clippy::for_loop_over_option)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` + +error: lint `clippy::for_loop_over_result` has been renamed to `clippy::for_loops_over_fallibles` + --> $DIR/rename.rs:43:9 + | +LL | #![warn(clippy::for_loop_over_result)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` + +error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` + --> $DIR/rename.rs:44:9 + | +LL | #![warn(clippy::identity_conversion)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` + +error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` + --> $DIR/rename.rs:45:9 + | +LL | #![warn(clippy::if_let_some_result)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` + +error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` + --> $DIR/rename.rs:46:9 + | +LL | #![warn(clippy::new_without_default_derive)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` + error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> $DIR/rename.rs:39:9 + --> $DIR/rename.rs:47:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` -error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> $DIR/rename.rs:40:9 - | -LL | #![warn(clippy::box_vec)] - | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` - -error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:41:9 - | -LL | #![warn(clippy::block_in_if_condition_expr)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` - -error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:42:9 - | -LL | #![warn(clippy::block_in_if_condition_stmt)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` - -error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:43:9 - | -LL | #![warn(clippy::option_map_unwrap_or)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` - -error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:44:9 - | -LL | #![warn(clippy::option_map_unwrap_or_else)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` - -error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:45:9 - | -LL | #![warn(clippy::result_map_unwrap_or_else)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` - -error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:46:9 - | -LL | #![warn(clippy::option_unwrap_used)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` - -error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:47:9 - | -LL | #![warn(clippy::result_unwrap_used)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` - error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` --> $DIR/rename.rs:48:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` -error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` +error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` --> $DIR/rename.rs:49:9 | -LL | #![warn(clippy::result_expect_used)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` +LL | #![warn(clippy::option_map_unwrap_or)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` -error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles` +error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` --> $DIR/rename.rs:50:9 | -LL | #![warn(clippy::for_loop_over_option)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` +LL | #![warn(clippy::option_map_unwrap_or_else)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` -error: lint `clippy::for_loop_over_result` has been renamed to `clippy::for_loops_over_fallibles` +error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` --> $DIR/rename.rs:51:9 | -LL | #![warn(clippy::for_loop_over_result)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` - -error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> $DIR/rename.rs:52:9 - | -LL | #![warn(clippy::identity_conversion)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` - -error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> $DIR/rename.rs:53:9 - | -LL | #![warn(clippy::zero_width_space)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` - -error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> $DIR/rename.rs:54:9 - | -LL | #![warn(clippy::single_char_push_str)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` - -error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> $DIR/rename.rs:55:9 - | -LL | #![warn(clippy::if_let_some_result)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` - -error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> $DIR/rename.rs:56:9 - | -LL | #![warn(clippy::disallowed_type)] - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` - -error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> $DIR/rename.rs:57:9 - | -LL | #![warn(clippy::disallowed_method)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` +LL | #![warn(clippy::option_unwrap_used)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> $DIR/rename.rs:58:9 + --> $DIR/rename.rs:52:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` +error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` + --> $DIR/rename.rs:53:9 + | +LL | #![warn(clippy::result_expect_used)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` + +error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` + --> $DIR/rename.rs:54:9 + | +LL | #![warn(clippy::result_map_unwrap_or_else)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` + +error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` + --> $DIR/rename.rs:55:9 + | +LL | #![warn(clippy::result_unwrap_used)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` + +error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` + --> $DIR/rename.rs:56:9 + | +LL | #![warn(clippy::single_char_push_str)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` + +error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` + --> $DIR/rename.rs:57:9 + | +LL | #![warn(clippy::stutter)] + | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` + error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> $DIR/rename.rs:59:9 + --> $DIR/rename.rs:58:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` -error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> $DIR/rename.rs:61:9 +error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` + --> $DIR/rename.rs:59:9 | -LL | #![warn(clippy::invalid_ref)] - | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` - -error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> $DIR/rename.rs:62:9 - | -LL | #![warn(clippy::into_iter_on_array)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` - -error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> $DIR/rename.rs:63:9 - | -LL | #![warn(clippy::unused_label)] - | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` +LL | #![warn(clippy::zero_width_space)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> $DIR/rename.rs:64:9 + --> $DIR/rename.rs:60:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` -error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` - --> $DIR/rename.rs:65:9 +error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` + --> $DIR/rename.rs:61:9 | -LL | #![warn(clippy::temporary_cstring_as_ptr)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` +LL | #![warn(clippy::into_iter_on_array)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` + +error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` + --> $DIR/rename.rs:62:9 + | +LL | #![warn(clippy::invalid_atomic_ordering)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` + +error: lint `clippy::invalid_ref` has been renamed to `invalid_value` + --> $DIR/rename.rs:63:9 + | +LL | #![warn(clippy::invalid_ref)] + | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` + +error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` + --> $DIR/rename.rs:64:9 + | +LL | #![warn(clippy::mem_discriminant_non_enum)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> $DIR/rename.rs:66:9 + --> $DIR/rename.rs:65:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` +error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` + --> $DIR/rename.rs:66:9 + | +LL | #![warn(clippy::temporary_cstring_as_ptr)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` + error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` --> $DIR/rename.rs:67:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` -error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` +error: lint `clippy::unused_label` has been renamed to `unused_labels` --> $DIR/rename.rs:68:9 | -LL | #![warn(clippy::invalid_atomic_ordering)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` - -error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> $DIR/rename.rs:69:9 - | -LL | #![warn(clippy::mem_discriminant_non_enum)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` +LL | #![warn(clippy::unused_label)] + | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` error: aborting due to 34 previous errors diff --git a/tests/ui/rest_pat_in_fully_bound_structs.rs b/tests/ui/rest_pat_in_fully_bound_structs.rs index 38fc9969804..086331af6b5 100644 --- a/tests/ui/rest_pat_in_fully_bound_structs.rs +++ b/tests/ui/rest_pat_in_fully_bound_structs.rs @@ -39,4 +39,19 @@ fn main() { // No lint foo!(a_struct); + + #[non_exhaustive] + struct B { + a: u32, + b: u32, + c: u64, + } + + let b_struct = B { a: 5, b: 42, c: 342 }; + + match b_struct { + B { a: 5, b: 42, .. } => {}, + B { a: 0, b: 0, c: 128, .. } => {}, // No Lint + _ => {}, + } } diff --git a/tests/ui/same_functions_in_if_condition.rs b/tests/ui/same_functions_in_if_condition.rs index 7f28f025790..3d2295912c9 100644 --- a/tests/ui/same_functions_in_if_condition.rs +++ b/tests/ui/same_functions_in_if_condition.rs @@ -1,3 +1,5 @@ +#![feature(adt_const_params)] +#![allow(incomplete_features)] #![warn(clippy::same_functions_in_if_condition)] #![allow(clippy::ifs_same_cond)] // This warning is different from `ifs_same_cond`. #![allow(clippy::if_same_then_else, clippy::comparison_chain)] // all empty blocks @@ -87,4 +89,21 @@ fn main() { "linux" }; println!("{}", os); + + #[derive(PartialEq, Eq)] + enum E { + A, + B, + } + fn generic() -> bool { + match P { + E::A => true, + E::B => false, + } + } + if generic::<{ E::A }>() { + println!("A"); + } else if generic::<{ E::B }>() { + println!("B"); + } } diff --git a/tests/ui/same_functions_in_if_condition.stderr b/tests/ui/same_functions_in_if_condition.stderr index 363a03846d2..71e82910ef7 100644 --- a/tests/ui/same_functions_in_if_condition.stderr +++ b/tests/ui/same_functions_in_if_condition.stderr @@ -1,72 +1,72 @@ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:29:15 + --> $DIR/same_functions_in_if_condition.rs:31:15 | LL | } else if function() { | ^^^^^^^^^^ | = note: `-D clippy::same-functions-in-if-condition` implied by `-D warnings` note: same as this - --> $DIR/same_functions_in_if_condition.rs:28:8 + --> $DIR/same_functions_in_if_condition.rs:30:8 | LL | if function() { | ^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:34:15 + --> $DIR/same_functions_in_if_condition.rs:36:15 | LL | } else if fn_arg(a) { | ^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:33:8 + --> $DIR/same_functions_in_if_condition.rs:35:8 | LL | if fn_arg(a) { | ^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:39:15 + --> $DIR/same_functions_in_if_condition.rs:41:15 | LL | } else if obj.method() { | ^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:38:8 + --> $DIR/same_functions_in_if_condition.rs:40:8 | LL | if obj.method() { | ^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:44:15 + --> $DIR/same_functions_in_if_condition.rs:46:15 | LL | } else if obj.method_arg(a) { | ^^^^^^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:43:8 + --> $DIR/same_functions_in_if_condition.rs:45:8 | LL | if obj.method_arg(a) { | ^^^^^^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:51:15 + --> $DIR/same_functions_in_if_condition.rs:53:15 | LL | } else if v.pop() == None { | ^^^^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:49:8 + --> $DIR/same_functions_in_if_condition.rs:51:8 | LL | if v.pop() == None { | ^^^^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:56:15 + --> $DIR/same_functions_in_if_condition.rs:58:15 | LL | } else if v.len() == 42 { | ^^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:54:8 + --> $DIR/same_functions_in_if_condition.rs:56:8 | LL | if v.len() == 42 { | ^^^^^^^^^^^^^ diff --git a/tests/ui/shadow.rs b/tests/ui/shadow.rs index 0321f8c4cdf..a394ef8f25c 100644 --- a/tests/ui/shadow.rs +++ b/tests/ui/shadow.rs @@ -1,4 +1,5 @@ #![warn(clippy::shadow_same, clippy::shadow_reuse, clippy::shadow_unrelated)] +#![allow(clippy::let_unit_value)] fn shadow_same() { let x = 1; diff --git a/tests/ui/shadow.stderr b/tests/ui/shadow.stderr index f8b9221d555..3bd41d06260 100644 --- a/tests/ui/shadow.stderr +++ b/tests/ui/shadow.stderr @@ -1,266 +1,266 @@ error: `x` is shadowed by itself in `x` - --> $DIR/shadow.rs:5:9 + --> $DIR/shadow.rs:6:9 | LL | let x = x; | ^ | = note: `-D clippy::shadow-same` implied by `-D warnings` note: previous binding is here - --> $DIR/shadow.rs:4:9 + --> $DIR/shadow.rs:5:9 | LL | let x = 1; | ^ error: `mut x` is shadowed by itself in `&x` - --> $DIR/shadow.rs:6:13 + --> $DIR/shadow.rs:7:13 | LL | let mut x = &x; | ^ | note: previous binding is here - --> $DIR/shadow.rs:5:9 + --> $DIR/shadow.rs:6:9 | LL | let x = x; | ^ error: `x` is shadowed by itself in `&mut x` - --> $DIR/shadow.rs:7:9 + --> $DIR/shadow.rs:8:9 | LL | let x = &mut x; | ^ | note: previous binding is here - --> $DIR/shadow.rs:6:9 + --> $DIR/shadow.rs:7:9 | LL | let mut x = &x; | ^^^^^ error: `x` is shadowed by itself in `*x` - --> $DIR/shadow.rs:8:9 + --> $DIR/shadow.rs:9:9 | LL | let x = *x; | ^ | note: previous binding is here - --> $DIR/shadow.rs:7:9 + --> $DIR/shadow.rs:8:9 | LL | let x = &mut x; | ^ error: `x` is shadowed - --> $DIR/shadow.rs:13:9 + --> $DIR/shadow.rs:14:9 | LL | let x = x.0; | ^ | = note: `-D clippy::shadow-reuse` implied by `-D warnings` note: previous binding is here - --> $DIR/shadow.rs:12:9 + --> $DIR/shadow.rs:13:9 | LL | let x = ([[0]], ()); | ^ error: `x` is shadowed - --> $DIR/shadow.rs:14:9 + --> $DIR/shadow.rs:15:9 | LL | let x = x[0]; | ^ | note: previous binding is here - --> $DIR/shadow.rs:13:9 + --> $DIR/shadow.rs:14:9 | LL | let x = x.0; | ^ error: `x` is shadowed - --> $DIR/shadow.rs:15:10 + --> $DIR/shadow.rs:16:10 | LL | let [x] = x; | ^ | note: previous binding is here - --> $DIR/shadow.rs:14:9 + --> $DIR/shadow.rs:15:9 | LL | let x = x[0]; | ^ error: `x` is shadowed - --> $DIR/shadow.rs:16:9 + --> $DIR/shadow.rs:17:9 | LL | let x = Some(x); | ^ | note: previous binding is here - --> $DIR/shadow.rs:15:10 + --> $DIR/shadow.rs:16:10 | LL | let [x] = x; | ^ error: `x` is shadowed - --> $DIR/shadow.rs:17:9 + --> $DIR/shadow.rs:18:9 | LL | let x = foo(x); | ^ | note: previous binding is here - --> $DIR/shadow.rs:16:9 + --> $DIR/shadow.rs:17:9 | LL | let x = Some(x); | ^ error: `x` is shadowed - --> $DIR/shadow.rs:18:9 + --> $DIR/shadow.rs:19:9 | LL | let x = || x; | ^ | note: previous binding is here - --> $DIR/shadow.rs:17:9 + --> $DIR/shadow.rs:18:9 | LL | let x = foo(x); | ^ error: `x` is shadowed - --> $DIR/shadow.rs:19:9 + --> $DIR/shadow.rs:20:9 | LL | let x = Some(1).map(|_| x)?; | ^ | note: previous binding is here - --> $DIR/shadow.rs:18:9 + --> $DIR/shadow.rs:19:9 | LL | let x = || x; | ^ error: `y` is shadowed - --> $DIR/shadow.rs:21:9 + --> $DIR/shadow.rs:22:9 | LL | let y = match y { | ^ | note: previous binding is here - --> $DIR/shadow.rs:20:9 + --> $DIR/shadow.rs:21:9 | LL | let y = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:30:9 + --> $DIR/shadow.rs:31:9 | LL | let x = 2; | ^ | = note: `-D clippy::shadow-unrelated` implied by `-D warnings` note: previous binding is here - --> $DIR/shadow.rs:29:9 + --> $DIR/shadow.rs:30:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:35:13 + --> $DIR/shadow.rs:36:13 | LL | let x = 1; | ^ | note: previous binding is here - --> $DIR/shadow.rs:34:10 + --> $DIR/shadow.rs:35:10 | LL | fn f(x: u32) { | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:40:14 + --> $DIR/shadow.rs:41:14 | LL | Some(x) => { | ^ | note: previous binding is here - --> $DIR/shadow.rs:37:9 + --> $DIR/shadow.rs:38:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:41:17 + --> $DIR/shadow.rs:42:17 | LL | let x = 1; | ^ | note: previous binding is here - --> $DIR/shadow.rs:40:14 + --> $DIR/shadow.rs:41:14 | LL | Some(x) => { | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:45:17 + --> $DIR/shadow.rs:46:17 | LL | if let Some(x) = Some(1) {} | ^ | note: previous binding is here - --> $DIR/shadow.rs:37:9 + --> $DIR/shadow.rs:38:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:46:20 + --> $DIR/shadow.rs:47:20 | LL | while let Some(x) = Some(1) {} | ^ | note: previous binding is here - --> $DIR/shadow.rs:37:9 + --> $DIR/shadow.rs:38:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:47:15 + --> $DIR/shadow.rs:48:15 | LL | let _ = |[x]: [u32; 1]| { | ^ | note: previous binding is here - --> $DIR/shadow.rs:37:9 + --> $DIR/shadow.rs:38:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:48:13 + --> $DIR/shadow.rs:49:13 | LL | let x = 1; | ^ | note: previous binding is here - --> $DIR/shadow.rs:47:15 + --> $DIR/shadow.rs:48:15 | LL | let _ = |[x]: [u32; 1]| { | ^ error: `y` is shadowed - --> $DIR/shadow.rs:51:17 + --> $DIR/shadow.rs:52:17 | LL | if let Some(y) = y {} | ^ | note: previous binding is here - --> $DIR/shadow.rs:50:9 + --> $DIR/shadow.rs:51:9 | LL | let y = Some(1); | ^ error: `_b` shadows a previous, unrelated binding - --> $DIR/shadow.rs:87:9 + --> $DIR/shadow.rs:88:9 | LL | let _b = _a; | ^^ | note: previous binding is here - --> $DIR/shadow.rs:86:28 + --> $DIR/shadow.rs:87:28 | LL | pub async fn foo2(_a: i32, _b: i64) { | ^^ diff --git a/tests/ui/similar_names.rs b/tests/ui/similar_names.rs index 76f6ce9ee6b..c21225d153b 100644 --- a/tests/ui/similar_names.rs +++ b/tests/ui/similar_names.rs @@ -3,7 +3,8 @@ unused, clippy::println_empty_string, clippy::empty_loop, - clippy::diverging_sub_expression + clippy::diverging_sub_expression, + clippy::let_unit_value )] struct Foo { diff --git a/tests/ui/similar_names.stderr b/tests/ui/similar_names.stderr index faf572b0c6b..6e772693897 100644 --- a/tests/ui/similar_names.stderr +++ b/tests/ui/similar_names.stderr @@ -1,84 +1,84 @@ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:20:9 + --> $DIR/similar_names.rs:21:9 | LL | let bpple: i32; | ^^^^^ | = note: `-D clippy::similar-names` implied by `-D warnings` note: existing binding defined here - --> $DIR/similar_names.rs:18:9 + --> $DIR/similar_names.rs:19:9 | LL | let apple: i32; | ^^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:22:9 + --> $DIR/similar_names.rs:23:9 | LL | let cpple: i32; | ^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:18:9 + --> $DIR/similar_names.rs:19:9 | LL | let apple: i32; | ^^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:46:9 + --> $DIR/similar_names.rs:47:9 | LL | let bluby: i32; | ^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:45:9 + --> $DIR/similar_names.rs:46:9 | LL | let blubx: i32; | ^^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:50:9 + --> $DIR/similar_names.rs:51:9 | LL | let coke: i32; | ^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:48:9 + --> $DIR/similar_names.rs:49:9 | LL | let cake: i32; | ^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:68:9 + --> $DIR/similar_names.rs:69:9 | LL | let xyzeabc: i32; | ^^^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:66:9 + --> $DIR/similar_names.rs:67:9 | LL | let xyz1abc: i32; | ^^^^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:72:9 + --> $DIR/similar_names.rs:73:9 | LL | let parsee: i32; | ^^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:70:9 + --> $DIR/similar_names.rs:71:9 | LL | let parser: i32; | ^^^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:93:16 + --> $DIR/similar_names.rs:94:16 | LL | bpple: sprang, | ^^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:92:16 + --> $DIR/similar_names.rs:93:16 | LL | apple: spring, | ^^^^^^ diff --git a/tests/ui/single_char_lifetime_names.rs b/tests/ui/single_char_lifetime_names.rs index 261d8bc7260..69c5b236f7c 100644 --- a/tests/ui/single_char_lifetime_names.rs +++ b/tests/ui/single_char_lifetime_names.rs @@ -1,4 +1,5 @@ #![warn(clippy::single_char_lifetime_names)] +#![allow(clippy::let_unit_value)] // Lifetimes should only be linted when they're introduced struct DiagnosticCtx<'a, 'b> diff --git a/tests/ui/single_char_lifetime_names.stderr b/tests/ui/single_char_lifetime_names.stderr index 013b64f46a8..1438b3999db 100644 --- a/tests/ui/single_char_lifetime_names.stderr +++ b/tests/ui/single_char_lifetime_names.stderr @@ -1,5 +1,5 @@ error: single-character lifetime names are likely uninformative - --> $DIR/single_char_lifetime_names.rs:4:22 + --> $DIR/single_char_lifetime_names.rs:5:22 | LL | struct DiagnosticCtx<'a, 'b> | ^^ @@ -8,7 +8,7 @@ LL | struct DiagnosticCtx<'a, 'b> = help: use a more informative name error: single-character lifetime names are likely uninformative - --> $DIR/single_char_lifetime_names.rs:4:26 + --> $DIR/single_char_lifetime_names.rs:5:26 | LL | struct DiagnosticCtx<'a, 'b> | ^^ @@ -16,7 +16,7 @@ LL | struct DiagnosticCtx<'a, 'b> = help: use a more informative name error: single-character lifetime names are likely uninformative - --> $DIR/single_char_lifetime_names.rs:13:6 + --> $DIR/single_char_lifetime_names.rs:14:6 | LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> { | ^^ @@ -24,7 +24,7 @@ LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> { = help: use a more informative name error: single-character lifetime names are likely uninformative - --> $DIR/single_char_lifetime_names.rs:13:10 + --> $DIR/single_char_lifetime_names.rs:14:10 | LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> { | ^^ @@ -32,7 +32,7 @@ LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> { = help: use a more informative name error: single-character lifetime names are likely uninformative - --> $DIR/single_char_lifetime_names.rs:33:15 + --> $DIR/single_char_lifetime_names.rs:34:15 | LL | fn split_once<'a>(base: &'a str, other: &'_ str) -> (&'a str, Option<&'a str>) { | ^^ diff --git a/tests/ui/single_match_else.rs b/tests/ui/single_match_else.rs index b624a41a29b..82387f3d80b 100644 --- a/tests/ui/single_match_else.rs +++ b/tests/ui/single_match_else.rs @@ -1,7 +1,12 @@ +// aux-build: proc_macro_with_span.rs + #![warn(clippy::single_match_else)] #![allow(clippy::needless_return)] #![allow(clippy::no_effect)] +extern crate proc_macro_with_span; +use proc_macro_with_span::with_span; + enum ExprNode { ExprAddrOf, Butterflies, @@ -11,13 +16,22 @@ enum ExprNode { static NODE: ExprNode = ExprNode::Unicorns; fn unwrap_addr() -> Option<&'static ExprNode> { - match ExprNode::Butterflies { + let _ = match ExprNode::Butterflies { ExprNode::ExprAddrOf => Some(&NODE), _ => { let x = 5; None }, - } + }; + + // Don't lint + with_span!(span match ExprNode::Butterflies { + ExprNode::ExprAddrOf => Some(&NODE), + _ => { + let x = 5; + None + }, + }) } macro_rules! unwrap_addr { diff --git a/tests/ui/single_match_else.stderr b/tests/ui/single_match_else.stderr index 21ea704b62a..7756c6f204e 100644 --- a/tests/ui/single_match_else.stderr +++ b/tests/ui/single_match_else.stderr @@ -1,22 +1,23 @@ error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match_else.rs:14:5 + --> $DIR/single_match_else.rs:19:13 | -LL | / match ExprNode::Butterflies { +LL | let _ = match ExprNode::Butterflies { + | _____________^ LL | | ExprNode::ExprAddrOf => Some(&NODE), LL | | _ => { LL | | let x = 5; LL | | None LL | | }, -LL | | } +LL | | }; | |_____^ | = note: `-D clippy::single-match-else` implied by `-D warnings` help: try this | -LL ~ if let ExprNode::ExprAddrOf = ExprNode::Butterflies { Some(&NODE) } else { +LL ~ let _ = if let ExprNode::ExprAddrOf = ExprNode::Butterflies { Some(&NODE) } else { LL + let x = 5; LL + None -LL + } +LL ~ }; | error: aborting due to previous error diff --git a/tests/ui/stable_sort_primitive.stderr b/tests/ui/stable_sort_primitive.stderr index b8d22ed2504..c35e0c22ae8 100644 --- a/tests/ui/stable_sort_primitive.stderr +++ b/tests/ui/stable_sort_primitive.stderr @@ -5,7 +5,7 @@ LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` | = note: `-D clippy::stable-sort-primitive` implied by `-D warnings` - = note: an unstable sort would perform faster without any observable difference for this data type + = note: an unstable sort typically performs faster without any observable difference for this data type error: used `sort` on primitive type `bool` --> $DIR/stable_sort_primitive.rs:9:5 @@ -13,7 +13,7 @@ error: used `sort` on primitive type `bool` LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` | - = note: an unstable sort would perform faster without any observable difference for this data type + = note: an unstable sort typically performs faster without any observable difference for this data type error: used `sort` on primitive type `char` --> $DIR/stable_sort_primitive.rs:11:5 @@ -21,7 +21,7 @@ error: used `sort` on primitive type `char` LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` | - = note: an unstable sort would perform faster without any observable difference for this data type + = note: an unstable sort typically performs faster without any observable difference for this data type error: used `sort` on primitive type `str` --> $DIR/stable_sort_primitive.rs:13:5 @@ -29,7 +29,7 @@ error: used `sort` on primitive type `str` LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` | - = note: an unstable sort would perform faster without any observable difference for this data type + = note: an unstable sort typically performs faster without any observable difference for this data type error: used `sort` on primitive type `tuple` --> $DIR/stable_sort_primitive.rs:15:5 @@ -37,7 +37,7 @@ error: used `sort` on primitive type `tuple` LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` | - = note: an unstable sort would perform faster without any observable difference for this data type + = note: an unstable sort typically performs faster without any observable difference for this data type error: used `sort` on primitive type `array` --> $DIR/stable_sort_primitive.rs:17:5 @@ -45,7 +45,7 @@ error: used `sort` on primitive type `array` LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` | - = note: an unstable sort would perform faster without any observable difference for this data type + = note: an unstable sort typically performs faster without any observable difference for this data type error: used `sort` on primitive type `i32` --> $DIR/stable_sort_primitive.rs:19:5 @@ -53,7 +53,7 @@ error: used `sort` on primitive type `i32` LL | arr.sort(); | ^^^^^^^^^^ help: try: `arr.sort_unstable()` | - = note: an unstable sort would perform faster without any observable difference for this data type + = note: an unstable sort typically performs faster without any observable difference for this data type error: aborting due to 7 previous errors diff --git a/tests/ui/suspicious_else_formatting.rs b/tests/ui/suspicious_else_formatting.rs index fcd827a91c7..21753e5dc6a 100644 --- a/tests/ui/suspicious_else_formatting.rs +++ b/tests/ui/suspicious_else_formatting.rs @@ -1,7 +1,7 @@ // aux-build:proc_macro_suspicious_else_formatting.rs #![warn(clippy::suspicious_else_formatting)] -#![allow(clippy::if_same_then_else)] +#![allow(clippy::if_same_then_else, clippy::let_unit_value)] extern crate proc_macro_suspicious_else_formatting; use proc_macro_suspicious_else_formatting::DeriveBadSpan; diff --git a/tests/ui/to_digit_is_some.fixed b/tests/ui/to_digit_is_some.fixed index 19184df0bec..3c5e9642714 100644 --- a/tests/ui/to_digit_is_some.fixed +++ b/tests/ui/to_digit_is_some.fixed @@ -6,6 +6,6 @@ fn main() { let c = 'x'; let d = &c; - let _ = d.is_digit(10); - let _ = char::is_digit(c, 10); + let _ = d.is_digit(8); + let _ = char::is_digit(c, 8); } diff --git a/tests/ui/to_digit_is_some.rs b/tests/ui/to_digit_is_some.rs index 45a6728ebf5..4f247c06cee 100644 --- a/tests/ui/to_digit_is_some.rs +++ b/tests/ui/to_digit_is_some.rs @@ -6,6 +6,6 @@ fn main() { let c = 'x'; let d = &c; - let _ = d.to_digit(10).is_some(); - let _ = char::to_digit(c, 10).is_some(); + let _ = d.to_digit(8).is_some(); + let _ = char::to_digit(c, 8).is_some(); } diff --git a/tests/ui/to_digit_is_some.stderr b/tests/ui/to_digit_is_some.stderr index 177d3ccd3e2..10a1b393a39 100644 --- a/tests/ui/to_digit_is_some.stderr +++ b/tests/ui/to_digit_is_some.stderr @@ -1,16 +1,16 @@ error: use of `.to_digit(..).is_some()` --> $DIR/to_digit_is_some.rs:9:13 | -LL | let _ = d.to_digit(10).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `d.is_digit(10)` +LL | let _ = d.to_digit(8).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `d.is_digit(8)` | = note: `-D clippy::to-digit-is-some` implied by `-D warnings` error: use of `.to_digit(..).is_some()` --> $DIR/to_digit_is_some.rs:10:13 | -LL | let _ = char::to_digit(c, 10).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `char::is_digit(c, 10)` +LL | let _ = char::to_digit(c, 8).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `char::is_digit(c, 8)` error: aborting due to 2 previous errors diff --git a/tests/ui/trait_duplication_in_bounds.rs b/tests/ui/trait_duplication_in_bounds.rs index f5ca91143af..a21d4c5d637 100644 --- a/tests/ui/trait_duplication_in_bounds.rs +++ b/tests/ui/trait_duplication_in_bounds.rs @@ -95,4 +95,7 @@ trait FooIter: Iterator { } } +// This should not lint +fn impl_trait(_: impl AsRef, _: impl AsRef) {} + fn main() {} diff --git a/tests/ui/trait_duplication_in_bounds.stderr b/tests/ui/trait_duplication_in_bounds.stderr index 6f8c8e47dfb..d0a4cfb8837 100644 --- a/tests/ui/trait_duplication_in_bounds.stderr +++ b/tests/ui/trait_duplication_in_bounds.stderr @@ -67,5 +67,13 @@ LL | Self: Iterator, | = help: consider removing this trait bound -error: aborting due to 8 previous errors +error: this trait bound is already specified in the where clause + --> $DIR/trait_duplication_in_bounds.rs:99:23 + | +LL | fn impl_trait(_: impl AsRef, _: impl AsRef) {} + | ^^^^^^^^^^ + | + = help: consider removing this trait bound + +error: aborting due to 9 previous errors diff --git a/tests/ui/transmute_collection.rs b/tests/ui/transmute_collection.rs index cd5a7127791..5a431bee04a 100644 --- a/tests/ui/transmute_collection.rs +++ b/tests/ui/transmute_collection.rs @@ -1,7 +1,7 @@ #![warn(clippy::unsound_collection_transmute)] use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque}; -use std::mem::transmute; +use std::mem::{transmute, MaybeUninit}; fn main() { unsafe { @@ -43,5 +43,8 @@ fn main() { // wrong layout let _ = transmute::<_, HashMap>(HashMap::::new()); let _ = transmute::<_, HashMap>(HashMap::<[u8; 4], u32>::new()); + + let _ = transmute::<_, Vec>(Vec::>::new()); + let _ = transmute::<_, Vec<*mut u32>>(Vec::>::new()); } } diff --git a/tests/ui/trim_split_whitespace.fixed b/tests/ui/trim_split_whitespace.fixed new file mode 100644 index 00000000000..e4d352f7367 --- /dev/null +++ b/tests/ui/trim_split_whitespace.fixed @@ -0,0 +1,91 @@ +// run-rustfix +#![warn(clippy::trim_split_whitespace)] +#![allow(clippy::let_unit_value)] + +struct Custom; +impl Custom { + fn trim(self) -> Self { + self + } + fn split_whitespace(self) {} +} + +struct DerefStr(&'static str); +impl std::ops::Deref for DerefStr { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} + +struct DerefStrAndCustom(&'static str); +impl std::ops::Deref for DerefStrAndCustom { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} +impl DerefStrAndCustom { + fn trim(self) -> Self { + self + } + fn split_whitespace(self) {} +} + +struct DerefStrAndCustomSplit(&'static str); +impl std::ops::Deref for DerefStrAndCustomSplit { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} +impl DerefStrAndCustomSplit { + #[allow(dead_code)] + fn split_whitespace(self) {} +} + +struct DerefStrAndCustomTrim(&'static str); +impl std::ops::Deref for DerefStrAndCustomTrim { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} +impl DerefStrAndCustomTrim { + fn trim(self) -> Self { + self + } +} + +fn main() { + // &str + let _ = " A B C ".split_whitespace(); // should trigger lint + let _ = " A B C ".split_whitespace(); // should trigger lint + let _ = " A B C ".split_whitespace(); // should trigger lint + + // String + let _ = (" A B C ").to_string().split_whitespace(); // should trigger lint + let _ = (" A B C ").to_string().split_whitespace(); // should trigger lint + let _ = (" A B C ").to_string().split_whitespace(); // should trigger lint + + // Custom + let _ = Custom.trim().split_whitespace(); // should not trigger lint + + // Deref + let s = DerefStr(" A B C "); + let _ = s.split_whitespace(); // should trigger lint + + // Deref + custom impl + let s = DerefStrAndCustom(" A B C "); + let _ = s.trim().split_whitespace(); // should not trigger lint + + // Deref + only custom split_ws() impl + let s = DerefStrAndCustomSplit(" A B C "); + let _ = s.split_whitespace(); // should trigger lint + // Expl: trim() is called on str (deref) and returns &str. + // Thus split_ws() is called on str as well and the custom impl on S is unused + + // Deref + only custom trim() impl + let s = DerefStrAndCustomTrim(" A B C "); + let _ = s.trim().split_whitespace(); // should not trigger lint +} diff --git a/tests/ui/trim_split_whitespace.rs b/tests/ui/trim_split_whitespace.rs new file mode 100644 index 00000000000..f98451a9837 --- /dev/null +++ b/tests/ui/trim_split_whitespace.rs @@ -0,0 +1,91 @@ +// run-rustfix +#![warn(clippy::trim_split_whitespace)] +#![allow(clippy::let_unit_value)] + +struct Custom; +impl Custom { + fn trim(self) -> Self { + self + } + fn split_whitespace(self) {} +} + +struct DerefStr(&'static str); +impl std::ops::Deref for DerefStr { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} + +struct DerefStrAndCustom(&'static str); +impl std::ops::Deref for DerefStrAndCustom { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} +impl DerefStrAndCustom { + fn trim(self) -> Self { + self + } + fn split_whitespace(self) {} +} + +struct DerefStrAndCustomSplit(&'static str); +impl std::ops::Deref for DerefStrAndCustomSplit { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} +impl DerefStrAndCustomSplit { + #[allow(dead_code)] + fn split_whitespace(self) {} +} + +struct DerefStrAndCustomTrim(&'static str); +impl std::ops::Deref for DerefStrAndCustomTrim { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} +impl DerefStrAndCustomTrim { + fn trim(self) -> Self { + self + } +} + +fn main() { + // &str + let _ = " A B C ".trim().split_whitespace(); // should trigger lint + let _ = " A B C ".trim_start().split_whitespace(); // should trigger lint + let _ = " A B C ".trim_end().split_whitespace(); // should trigger lint + + // String + let _ = (" A B C ").to_string().trim().split_whitespace(); // should trigger lint + let _ = (" A B C ").to_string().trim_start().split_whitespace(); // should trigger lint + let _ = (" A B C ").to_string().trim_end().split_whitespace(); // should trigger lint + + // Custom + let _ = Custom.trim().split_whitespace(); // should not trigger lint + + // Deref + let s = DerefStr(" A B C "); + let _ = s.trim().split_whitespace(); // should trigger lint + + // Deref + custom impl + let s = DerefStrAndCustom(" A B C "); + let _ = s.trim().split_whitespace(); // should not trigger lint + + // Deref + only custom split_ws() impl + let s = DerefStrAndCustomSplit(" A B C "); + let _ = s.trim().split_whitespace(); // should trigger lint + // Expl: trim() is called on str (deref) and returns &str. + // Thus split_ws() is called on str as well and the custom impl on S is unused + + // Deref + only custom trim() impl + let s = DerefStrAndCustomTrim(" A B C "); + let _ = s.trim().split_whitespace(); // should not trigger lint +} diff --git a/tests/ui/trim_split_whitespace.stderr b/tests/ui/trim_split_whitespace.stderr new file mode 100644 index 00000000000..5ae7849e27d --- /dev/null +++ b/tests/ui/trim_split_whitespace.stderr @@ -0,0 +1,52 @@ +error: found call to `str::trim` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:62:23 + | +LL | let _ = " A B C ".trim().split_whitespace(); // should trigger lint + | ^^^^^^^ help: remove `trim()` + | + = note: `-D clippy::trim-split-whitespace` implied by `-D warnings` + +error: found call to `str::trim_start` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:63:23 + | +LL | let _ = " A B C ".trim_start().split_whitespace(); // should trigger lint + | ^^^^^^^^^^^^^ help: remove `trim_start()` + +error: found call to `str::trim_end` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:64:23 + | +LL | let _ = " A B C ".trim_end().split_whitespace(); // should trigger lint + | ^^^^^^^^^^^ help: remove `trim_end()` + +error: found call to `str::trim` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:67:37 + | +LL | let _ = (" A B C ").to_string().trim().split_whitespace(); // should trigger lint + | ^^^^^^^ help: remove `trim()` + +error: found call to `str::trim_start` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:68:37 + | +LL | let _ = (" A B C ").to_string().trim_start().split_whitespace(); // should trigger lint + | ^^^^^^^^^^^^^ help: remove `trim_start()` + +error: found call to `str::trim_end` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:69:37 + | +LL | let _ = (" A B C ").to_string().trim_end().split_whitespace(); // should trigger lint + | ^^^^^^^^^^^ help: remove `trim_end()` + +error: found call to `str::trim` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:76:15 + | +LL | let _ = s.trim().split_whitespace(); // should trigger lint + | ^^^^^^^ help: remove `trim()` + +error: found call to `str::trim` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:84:15 + | +LL | let _ = s.trim().split_whitespace(); // should trigger lint + | ^^^^^^^ help: remove `trim()` + +error: aborting due to 8 previous errors + diff --git a/tests/ui/type_repetition_in_bounds.rs b/tests/ui/type_repetition_in_bounds.rs index fc740ee11d6..d11432f9046 100644 --- a/tests/ui/type_repetition_in_bounds.rs +++ b/tests/ui/type_repetition_in_bounds.rs @@ -79,4 +79,7 @@ where u: U, } +// This should not lint +fn impl_trait(_: impl AsRef, _: impl AsRef) {} + fn main() {} diff --git a/tests/ui/type_repetition_in_bounds.stderr b/tests/ui/type_repetition_in_bounds.stderr index 148c19c7d07..abc25e59496 100644 --- a/tests/ui/type_repetition_in_bounds.stderr +++ b/tests/ui/type_repetition_in_bounds.stderr @@ -19,5 +19,13 @@ LL | Self: Copy + Default + Ord, | = help: consider combining the bounds: `Self: Clone + Copy + Default + Ord` -error: aborting due to 2 previous errors +error: this type has already been used as a bound predicate + --> $DIR/type_repetition_in_bounds.rs:83:43 + | +LL | fn impl_trait(_: impl AsRef, _: impl AsRef) {} + | ^^^^^^^^^^ + | + = help: consider combining the bounds: `impl AsRef: AsRef + AsRef` + +error: aborting due to 3 previous errors diff --git a/tests/ui/undocumented_unsafe_blocks.rs b/tests/ui/undocumented_unsafe_blocks.rs index afa337c45f4..7be15b0b2dd 100644 --- a/tests/ui/undocumented_unsafe_blocks.rs +++ b/tests/ui/undocumented_unsafe_blocks.rs @@ -1,6 +1,7 @@ // aux-build:proc_macro_unsafe.rs #![warn(clippy::undocumented_unsafe_blocks)] +#![allow(clippy::let_unit_value)] extern crate proc_macro_unsafe; diff --git a/tests/ui/undocumented_unsafe_blocks.stderr b/tests/ui/undocumented_unsafe_blocks.stderr index 856a07fd316..87d445bd7b8 100644 --- a/tests/ui/undocumented_unsafe_blocks.stderr +++ b/tests/ui/undocumented_unsafe_blocks.stderr @@ -1,5 +1,5 @@ error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:256:19 + --> $DIR/undocumented_unsafe_blocks.rs:257:19 | LL | /* Safety: */ unsafe {} | ^^^^^^^^^ @@ -8,7 +8,7 @@ LL | /* Safety: */ unsafe {} = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:260:5 + --> $DIR/undocumented_unsafe_blocks.rs:261:5 | LL | unsafe {} | ^^^^^^^^^ @@ -16,7 +16,7 @@ LL | unsafe {} = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:264:14 + --> $DIR/undocumented_unsafe_blocks.rs:265:14 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:264:29 + --> $DIR/undocumented_unsafe_blocks.rs:265:29 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:264:48 + --> $DIR/undocumented_unsafe_blocks.rs:265:48 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:268:18 + --> $DIR/undocumented_unsafe_blocks.rs:269:18 | LL | let _ = (42, unsafe {}, "test", unsafe {}); | ^^^^^^^^^ @@ -48,7 +48,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:268:37 + --> $DIR/undocumented_unsafe_blocks.rs:269:37 | LL | let _ = (42, unsafe {}, "test", unsafe {}); | ^^^^^^^^^ @@ -56,7 +56,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:272:14 + --> $DIR/undocumented_unsafe_blocks.rs:273:14 | LL | let _ = *unsafe { &42 }; | ^^^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | let _ = *unsafe { &42 }; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:277:19 + --> $DIR/undocumented_unsafe_blocks.rs:278:19 | LL | let _ = match unsafe {} { | ^^^^^^^^^ @@ -72,7 +72,7 @@ LL | let _ = match unsafe {} { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:283:14 + --> $DIR/undocumented_unsafe_blocks.rs:284:14 | LL | let _ = &unsafe {}; | ^^^^^^^^^ @@ -80,7 +80,7 @@ LL | let _ = &unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:287:14 + --> $DIR/undocumented_unsafe_blocks.rs:288:14 | LL | let _ = [unsafe {}; 5]; | ^^^^^^^^^ @@ -88,7 +88,7 @@ LL | let _ = [unsafe {}; 5]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:291:13 + --> $DIR/undocumented_unsafe_blocks.rs:292:13 | LL | let _ = unsafe {}; | ^^^^^^^^^ @@ -96,7 +96,7 @@ LL | let _ = unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:301:8 + --> $DIR/undocumented_unsafe_blocks.rs:302:8 | LL | t!(unsafe {}); | ^^^^^^^^^ @@ -104,7 +104,7 @@ LL | t!(unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:307:13 + --> $DIR/undocumented_unsafe_blocks.rs:308:13 | LL | unsafe {} | ^^^^^^^^^ @@ -116,7 +116,7 @@ LL | t!(); = note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:315:5 + --> $DIR/undocumented_unsafe_blocks.rs:316:5 | LL | unsafe {} // SAFETY: | ^^^^^^^^^ @@ -124,7 +124,7 @@ LL | unsafe {} // SAFETY: = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:319:5 + --> $DIR/undocumented_unsafe_blocks.rs:320:5 | LL | unsafe { | ^^^^^^^^ @@ -132,7 +132,7 @@ LL | unsafe { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:329:5 + --> $DIR/undocumented_unsafe_blocks.rs:330:5 | LL | unsafe {}; | ^^^^^^^^^ @@ -140,7 +140,7 @@ LL | unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:333:20 + --> $DIR/undocumented_unsafe_blocks.rs:334:20 | LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/uninit.rs b/tests/ui/uninit.rs index 1ed3883c1f0..dac5ce272c0 100644 --- a/tests/ui/uninit.rs +++ b/tests/ui/uninit.rs @@ -1,4 +1,5 @@ #![feature(stmt_expr_attributes)] +#![allow(clippy::let_unit_value)] use std::mem::{self, MaybeUninit}; diff --git a/tests/ui/uninit.stderr b/tests/ui/uninit.stderr index 85b64a8419a..15ef2349489 100644 --- a/tests/ui/uninit.stderr +++ b/tests/ui/uninit.stderr @@ -1,5 +1,5 @@ error: this call for this type may be undefined behavior - --> $DIR/uninit.rs:6:29 + --> $DIR/uninit.rs:7:29 | LL | let _: usize = unsafe { MaybeUninit::uninit().assume_init() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,13 +7,13 @@ LL | let _: usize = unsafe { MaybeUninit::uninit().assume_init() }; = note: `#[deny(clippy::uninit_assumed_init)]` on by default error: this call for this type may be undefined behavior - --> $DIR/uninit.rs:9:31 + --> $DIR/uninit.rs:10:31 | LL | let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this call for this type may be undefined behavior - --> $DIR/uninit.rs:24:29 + --> $DIR/uninit.rs:25:29 | LL | let _: usize = unsafe { mem::MaybeUninit::uninit().assume_init() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index 535683729f6..38be87bddf1 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -7,7 +7,8 @@ clippy::unnecessary_wraps, clippy::or_fun_call, clippy::needless_question_mark, - clippy::self_named_constructors + clippy::self_named_constructors, + clippy::let_unit_value )] use std::fmt::Debug; diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index 5cfb367a7be..394dee29dc9 100644 --- a/tests/ui/unit_arg.stderr +++ b/tests/ui/unit_arg.stderr @@ -1,5 +1,5 @@ error: passing a unit value to a function - --> $DIR/unit_arg.rs:56:5 + --> $DIR/unit_arg.rs:57:5 | LL | / foo({ LL | | 1; @@ -20,7 +20,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:59:5 + --> $DIR/unit_arg.rs:60:5 | LL | foo(foo(1)); | ^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:60:5 + --> $DIR/unit_arg.rs:61:5 | LL | / foo({ LL | | foo(1); @@ -54,7 +54,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:65:5 + --> $DIR/unit_arg.rs:66:5 | LL | / b.bar({ LL | | 1; @@ -74,7 +74,7 @@ LL ~ b.bar(()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:68:5 + --> $DIR/unit_arg.rs:69:5 | LL | taking_multiple_units(foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -87,7 +87,7 @@ LL ~ taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:69:5 + --> $DIR/unit_arg.rs:70:5 | LL | / taking_multiple_units(foo(0), { LL | | foo(1); @@ -110,7 +110,7 @@ LL ~ taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:73:5 + --> $DIR/unit_arg.rs:74:5 | LL | / taking_multiple_units( LL | | { @@ -140,7 +140,7 @@ LL + foo(2); ... error: passing a unit value to a function - --> $DIR/unit_arg.rs:84:13 + --> $DIR/unit_arg.rs:85:13 | LL | None.or(Some(foo(2))); | ^^^^^^^^^^^^ @@ -154,7 +154,7 @@ LL ~ }); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:87:5 + --> $DIR/unit_arg.rs:88:5 | LL | foo(foo(())); | ^^^^^^^^^^^^ @@ -166,7 +166,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:124:5 + --> $DIR/unit_arg.rs:125:5 | LL | Some(foo(1)) | ^^^^^^^^^^^^ diff --git a/tests/ui/unit_hash.rs b/tests/ui/unit_hash.rs index 989916c239b..43eb54eff47 100644 --- a/tests/ui/unit_hash.rs +++ b/tests/ui/unit_hash.rs @@ -1,4 +1,5 @@ #![warn(clippy::unit_hash)] +#![allow(clippy::let_unit_value)] use std::collections::hash_map::DefaultHasher; use std::hash::Hash; diff --git a/tests/ui/unit_hash.stderr b/tests/ui/unit_hash.stderr index da276296e02..050fa55a12b 100644 --- a/tests/ui/unit_hash.stderr +++ b/tests/ui/unit_hash.stderr @@ -1,5 +1,5 @@ error: this call to `hash` on the unit type will do nothing - --> $DIR/unit_hash.rs:18:23 + --> $DIR/unit_hash.rs:19:23 | LL | Foo::Empty => ().hash(&mut state), | ^^^^^^^^^^^^^^^^^^^ help: remove the call to `hash` or consider using: `0_u8.hash(&mut state)` @@ -8,7 +8,7 @@ LL | Foo::Empty => ().hash(&mut state), = note: the implementation of `Hash` for `()` is a no-op error: this call to `hash` on the unit type will do nothing - --> $DIR/unit_hash.rs:23:5 + --> $DIR/unit_hash.rs:24:5 | LL | res.hash(&mut state); | ^^^^^^^^^^^^^^^^^^^^ help: remove the call to `hash` or consider using: `0_u8.hash(&mut state)` @@ -16,7 +16,7 @@ LL | res.hash(&mut state); = note: the implementation of `Hash` for `()` is a no-op error: this call to `hash` on the unit type will do nothing - --> $DIR/unit_hash.rs:26:5 + --> $DIR/unit_hash.rs:27:5 | LL | do_nothing().hash(&mut state); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `hash` or consider using: `0_u8.hash(&mut state)` diff --git a/tests/ui/unnecessary_owned_empty_strings.fixed b/tests/ui/unnecessary_owned_empty_strings.fixed new file mode 100644 index 00000000000..f95f91329a2 --- /dev/null +++ b/tests/ui/unnecessary_owned_empty_strings.fixed @@ -0,0 +1,22 @@ +// run-rustfix + +#![warn(clippy::unnecessary_owned_empty_strings)] + +fn ref_str_argument(_value: &str) {} + +#[allow(clippy::ptr_arg)] +fn ref_string_argument(_value: &String) {} + +fn main() { + // should be linted + ref_str_argument(""); + + // should be linted + ref_str_argument(""); + + // should not be linted + ref_str_argument(""); + + // should not be linted + ref_string_argument(&String::new()); +} diff --git a/tests/ui/unnecessary_owned_empty_strings.rs b/tests/ui/unnecessary_owned_empty_strings.rs new file mode 100644 index 00000000000..0cbdc151ed9 --- /dev/null +++ b/tests/ui/unnecessary_owned_empty_strings.rs @@ -0,0 +1,22 @@ +// run-rustfix + +#![warn(clippy::unnecessary_owned_empty_strings)] + +fn ref_str_argument(_value: &str) {} + +#[allow(clippy::ptr_arg)] +fn ref_string_argument(_value: &String) {} + +fn main() { + // should be linted + ref_str_argument(&String::new()); + + // should be linted + ref_str_argument(&String::from("")); + + // should not be linted + ref_str_argument(""); + + // should not be linted + ref_string_argument(&String::new()); +} diff --git a/tests/ui/unnecessary_owned_empty_strings.stderr b/tests/ui/unnecessary_owned_empty_strings.stderr new file mode 100644 index 00000000000..46bc4597b33 --- /dev/null +++ b/tests/ui/unnecessary_owned_empty_strings.stderr @@ -0,0 +1,16 @@ +error: usage of `&String::new()` for a function expecting a `&str` argument + --> $DIR/unnecessary_owned_empty_strings.rs:12:22 + | +LL | ref_str_argument(&String::new()); + | ^^^^^^^^^^^^^^ help: try: `""` + | + = note: `-D clippy::unnecessary-owned-empty-strings` implied by `-D warnings` + +error: usage of `&String::from("")` for a function expecting a `&str` argument + --> $DIR/unnecessary_owned_empty_strings.rs:15:22 + | +LL | ref_str_argument(&String::from("")); + | ^^^^^^^^^^^^^^^^^ help: try: `""` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed index 38ba41ac54e..7455e22d49b 100644 --- a/tests/ui/unnecessary_to_owned.fixed +++ b/tests/ui/unnecessary_to_owned.fixed @@ -2,6 +2,7 @@ #![allow(clippy::ptr_arg)] #![warn(clippy::unnecessary_to_owned)] +#![feature(custom_inner_attributes)] use std::borrow::Cow; use std::ffi::{CStr, CString, OsStr, OsString}; @@ -213,6 +214,17 @@ fn get_file_path(_file_type: &FileType) -> Result Result $DIR/unnecessary_to_owned.rs:150:64 + --> $DIR/unnecessary_to_owned.rs:151:64 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^ help: remove this | = note: `-D clippy::redundant-clone` implied by `-D warnings` note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:150:20 + --> $DIR/unnecessary_to_owned.rs:151:20 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:151:40 + --> $DIR/unnecessary_to_owned.rs:152:40 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:151:21 + --> $DIR/unnecessary_to_owned.rs:152:21 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:152:48 + --> $DIR/unnecessary_to_owned.rs:153:48 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:152:19 + --> $DIR/unnecessary_to_owned.rs:153:19 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:153:35 + --> $DIR/unnecessary_to_owned.rs:154:35 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:153:18 + --> $DIR/unnecessary_to_owned.rs:154:18 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^^^^^^ error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:59:36 + --> $DIR/unnecessary_to_owned.rs:60:36 | LL | require_c_str(&Cow::from(c_str).into_owned()); | ^^^^^^^^^^^^^ help: remove this @@ -56,427 +56,427 @@ LL | require_c_str(&Cow::from(c_str).into_owned()); = note: `-D clippy::unnecessary-to-owned` implied by `-D warnings` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:60:19 + --> $DIR/unnecessary_to_owned.rs:61:19 | LL | require_c_str(&c_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_os_string` - --> $DIR/unnecessary_to_owned.rs:62:20 + --> $DIR/unnecessary_to_owned.rs:63:20 | LL | require_os_str(&os_str.to_os_string()); | ^^^^^^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:63:38 + --> $DIR/unnecessary_to_owned.rs:64:38 | LL | require_os_str(&Cow::from(os_str).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:64:20 + --> $DIR/unnecessary_to_owned.rs:65:20 | LL | require_os_str(&os_str.to_owned()); | ^^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_path_buf` - --> $DIR/unnecessary_to_owned.rs:66:18 + --> $DIR/unnecessary_to_owned.rs:67:18 | LL | require_path(&path.to_path_buf()); | ^^^^^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:67:34 + --> $DIR/unnecessary_to_owned.rs:68:34 | LL | require_path(&Cow::from(path).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:68:18 + --> $DIR/unnecessary_to_owned.rs:69:18 | LL | require_path(&path.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:70:17 + --> $DIR/unnecessary_to_owned.rs:71:17 | LL | require_str(&s.to_string()); | ^^^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:71:30 + --> $DIR/unnecessary_to_owned.rs:72:30 | LL | require_str(&Cow::from(s).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:72:17 + --> $DIR/unnecessary_to_owned.rs:73:17 | LL | require_str(&s.to_owned()); | ^^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:73:17 + --> $DIR/unnecessary_to_owned.rs:74:17 | LL | require_str(&x_ref.to_string()); | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref.as_ref()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:75:19 + --> $DIR/unnecessary_to_owned.rs:76:19 | LL | require_slice(&slice.to_vec()); | ^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:76:36 + --> $DIR/unnecessary_to_owned.rs:77:36 | LL | require_slice(&Cow::from(slice).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:77:19 + --> $DIR/unnecessary_to_owned.rs:78:19 | LL | require_slice(&array.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `array.as_ref()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:78:19 + --> $DIR/unnecessary_to_owned.rs:79:19 | LL | require_slice(&array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref.as_ref()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:79:19 + --> $DIR/unnecessary_to_owned.rs:80:19 | LL | require_slice(&slice.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:80:19 + --> $DIR/unnecessary_to_owned.rs:81:19 | LL | require_slice(&x_ref.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `x_ref` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:82:42 + --> $DIR/unnecessary_to_owned.rs:83:42 | LL | require_x(&Cow::::Owned(x.clone()).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:83:15 + --> $DIR/unnecessary_to_owned.rs:84:15 | LL | require_x(&x_ref.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `x_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:85:25 + --> $DIR/unnecessary_to_owned.rs:86:25 | LL | require_deref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:86:26 + --> $DIR/unnecessary_to_owned.rs:87:26 | LL | require_deref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:87:24 + --> $DIR/unnecessary_to_owned.rs:88:24 | LL | require_deref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:88:23 + --> $DIR/unnecessary_to_owned.rs:89:23 | LL | require_deref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:89:25 + --> $DIR/unnecessary_to_owned.rs:90:25 | LL | require_deref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:91:30 + --> $DIR/unnecessary_to_owned.rs:92:30 | LL | require_impl_deref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:92:31 + --> $DIR/unnecessary_to_owned.rs:93:31 | LL | require_impl_deref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:93:29 + --> $DIR/unnecessary_to_owned.rs:94:29 | LL | require_impl_deref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:94:28 + --> $DIR/unnecessary_to_owned.rs:95:28 | LL | require_impl_deref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:95:30 + --> $DIR/unnecessary_to_owned.rs:96:30 | LL | require_impl_deref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:97:29 + --> $DIR/unnecessary_to_owned.rs:98:29 | LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:97:43 + --> $DIR/unnecessary_to_owned.rs:98:43 | LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:98:29 + --> $DIR/unnecessary_to_owned.rs:99:29 | LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:98:47 + --> $DIR/unnecessary_to_owned.rs:99:47 | LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:100:26 + --> $DIR/unnecessary_to_owned.rs:101:26 | LL | require_as_ref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:101:27 + --> $DIR/unnecessary_to_owned.rs:102:27 | LL | require_as_ref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:102:25 + --> $DIR/unnecessary_to_owned.rs:103:25 | LL | require_as_ref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:103:24 + --> $DIR/unnecessary_to_owned.rs:104:24 | LL | require_as_ref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:104:24 + --> $DIR/unnecessary_to_owned.rs:105:24 | LL | require_as_ref_str(x.to_owned()); | ^^^^^^^^^^^^ help: use: `&x` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:105:26 + --> $DIR/unnecessary_to_owned.rs:106:26 | LL | require_as_ref_slice(array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:106:26 + --> $DIR/unnecessary_to_owned.rs:107:26 | LL | require_as_ref_slice(array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:107:26 + --> $DIR/unnecessary_to_owned.rs:108:26 | LL | require_as_ref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:109:31 + --> $DIR/unnecessary_to_owned.rs:110:31 | LL | require_impl_as_ref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:110:32 + --> $DIR/unnecessary_to_owned.rs:111:32 | LL | require_impl_as_ref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:111:30 + --> $DIR/unnecessary_to_owned.rs:112:30 | LL | require_impl_as_ref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:112:29 + --> $DIR/unnecessary_to_owned.rs:113:29 | LL | require_impl_as_ref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:113:29 + --> $DIR/unnecessary_to_owned.rs:114:29 | LL | require_impl_as_ref_str(x.to_owned()); | ^^^^^^^^^^^^ help: use: `&x` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:114:31 + --> $DIR/unnecessary_to_owned.rs:115:31 | LL | require_impl_as_ref_slice(array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:115:31 + --> $DIR/unnecessary_to_owned.rs:116:31 | LL | require_impl_as_ref_slice(array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:116:31 + --> $DIR/unnecessary_to_owned.rs:117:31 | LL | require_impl_as_ref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` -error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:118:30 - | -LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); - | ^^^^^^^^^^^^ help: use: `s` - -error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:118:44 - | -LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); - | ^^^^^^^^^^^^^^^^ help: use: `array` - error: unnecessary use of `to_owned` --> $DIR/unnecessary_to_owned.rs:119:30 | -LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); +LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` --> $DIR/unnecessary_to_owned.rs:119:44 | -LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); - | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` +LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); + | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` --> $DIR/unnecessary_to_owned.rs:120:30 | -LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); +LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` --> $DIR/unnecessary_to_owned.rs:120:44 | +LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); + | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:121:30 + | +LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); + | ^^^^^^^^^^^^ help: use: `s` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:121:44 + | LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:121:30 + --> $DIR/unnecessary_to_owned.rs:122:30 | LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:121:48 + --> $DIR/unnecessary_to_owned.rs:122:48 | LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:122:30 + --> $DIR/unnecessary_to_owned.rs:123:30 | LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:122:52 + --> $DIR/unnecessary_to_owned.rs:123:52 | LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:123:30 + --> $DIR/unnecessary_to_owned.rs:124:30 | LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:123:48 + --> $DIR/unnecessary_to_owned.rs:124:48 | LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:125:20 + --> $DIR/unnecessary_to_owned.rs:126:20 | LL | let _ = x.join(&x_ref.to_string()); | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:127:13 + --> $DIR/unnecessary_to_owned.rs:128:13 | LL | let _ = slice.to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:128:13 + --> $DIR/unnecessary_to_owned.rs:129:13 | LL | let _ = slice.to_owned().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:129:13 + --> $DIR/unnecessary_to_owned.rs:130:13 | LL | let _ = [std::path::PathBuf::new()][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:130:13 + --> $DIR/unnecessary_to_owned.rs:131:13 | LL | let _ = [std::path::PathBuf::new()][..].to_owned().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:132:13 + --> $DIR/unnecessary_to_owned.rs:133:13 | LL | let _ = IntoIterator::into_iter(slice.to_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:133:13 + --> $DIR/unnecessary_to_owned.rs:134:13 | LL | let _ = IntoIterator::into_iter(slice.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:134:13 + --> $DIR/unnecessary_to_owned.rs:135:13 | LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:135:13 + --> $DIR/unnecessary_to_owned.rs:136:13 | LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:196:14 + --> $DIR/unnecessary_to_owned.rs:197:14 | LL | for t in file_types.to_vec() { | ^^^^^^^^^^^^^^^^^^^ @@ -491,11 +491,23 @@ LL - let path = match get_file_path(&t) { LL + let path = match get_file_path(t) { | +error: unnecessary use of `to_vec` + --> $DIR/unnecessary_to_owned.rs:220:14 + | +LL | let _ = &["x"][..].to_vec().into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().cloned()` + +error: unnecessary use of `to_vec` + --> $DIR/unnecessary_to_owned.rs:225:14 + | +LL | let _ = &["x"][..].to_vec().into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().copied()` + error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:260:24 + --> $DIR/unnecessary_to_owned.rs:272:24 | LL | Box::new(build(y.to_string())) | ^^^^^^^^^^^^^ help: use: `y` -error: aborting due to 77 previous errors +error: aborting due to 79 previous errors diff --git a/tests/ui/unnested_or_patterns.fixed b/tests/ui/unnested_or_patterns.fixed index 46463a29e9b..c223b5bc711 100644 --- a/tests/ui/unnested_or_patterns.fixed +++ b/tests/ui/unnested_or_patterns.fixed @@ -6,10 +6,13 @@ #![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] fn main() { + // Should be ignored by this lint, as nesting requires more characters. + if let &0 | &2 = &0 {} + if let box (0 | 2) = Box::new(0) {} if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {} - const C0: &u8 = &1; - if let &(0 | 2) | C0 = &0 {} + const C0: Option = Some(1); + if let Some(1 | 2) | C0 = None {} if let &mut (0 | 2) = &mut 0 {} if let x @ (0 | 2) = 0 {} if let (0, 1 | 2 | 3) = (0, 0) {} diff --git a/tests/ui/unnested_or_patterns.rs b/tests/ui/unnested_or_patterns.rs index 8ce0738bfc2..04cd11036e4 100644 --- a/tests/ui/unnested_or_patterns.rs +++ b/tests/ui/unnested_or_patterns.rs @@ -6,10 +6,13 @@ #![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] fn main() { + // Should be ignored by this lint, as nesting requires more characters. + if let &0 | &2 = &0 {} + if let box 0 | box 2 = Box::new(0) {} if let box ((0 | 1)) | box (2 | 3) | box 4 = Box::new(0) {} - const C0: &u8 = &1; - if let &0 | C0 | &2 = &0 {} + const C0: Option = Some(1); + if let Some(1) | C0 | Some(2) = None {} if let &mut 0 | &mut 2 = &mut 0 {} if let x @ 0 | x @ 2 = 0 {} if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {} diff --git a/tests/ui/unnested_or_patterns.stderr b/tests/ui/unnested_or_patterns.stderr index de424c3fdb8..453c66cbba8 100644 --- a/tests/ui/unnested_or_patterns.stderr +++ b/tests/ui/unnested_or_patterns.stderr @@ -1,5 +1,5 @@ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:9:12 + --> $DIR/unnested_or_patterns.rs:12:12 | LL | if let box 0 | box 2 = Box::new(0) {} | ^^^^^^^^^^^^^ @@ -11,7 +11,7 @@ LL | if let box (0 | 2) = Box::new(0) {} | ~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:10:12 + --> $DIR/unnested_or_patterns.rs:13:12 | LL | if let box ((0 | 1)) | box (2 | 3) | box 4 = Box::new(0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -22,18 +22,18 @@ LL | if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {} | ~~~~~~~~~~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:12:12 + --> $DIR/unnested_or_patterns.rs:15:12 | -LL | if let &0 | C0 | &2 = &0 {} - | ^^^^^^^^^^^^ +LL | if let Some(1) | C0 | Some(2) = None {} + | ^^^^^^^^^^^^^^^^^^^^^^ | help: nest the patterns | -LL | if let &(0 | 2) | C0 = &0 {} - | ~~~~~~~~~~~~~ +LL | if let Some(1 | 2) | C0 = None {} + | ~~~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:13:12 + --> $DIR/unnested_or_patterns.rs:16:12 | LL | if let &mut 0 | &mut 2 = &mut 0 {} | ^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | if let &mut (0 | 2) = &mut 0 {} | ~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:14:12 + --> $DIR/unnested_or_patterns.rs:17:12 | LL | if let x @ 0 | x @ 2 = 0 {} | ^^^^^^^^^^^^^ @@ -55,7 +55,7 @@ LL | if let x @ (0 | 2) = 0 {} | ~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:15:12 + --> $DIR/unnested_or_patterns.rs:18:12 | LL | if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -66,7 +66,7 @@ LL | if let (0, 1 | 2 | 3) = (0, 0) {} | ~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:16:12 + --> $DIR/unnested_or_patterns.rs:19:12 | LL | if let (1, 0) | (2, 0) | (3, 0) = (0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -77,7 +77,7 @@ LL | if let (1 | 2 | 3, 0) = (0, 0) {} | ~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:17:12 + --> $DIR/unnested_or_patterns.rs:20:12 | LL | if let (x, ..) | (x, 1) | (x, 2) = (0, 1) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | if let (x, ..) | (x, 1 | 2) = (0, 1) {} | ~~~~~~~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:18:12 + --> $DIR/unnested_or_patterns.rs:21:12 | LL | if let [0] | [1] = [0] {} | ^^^^^^^^^ @@ -99,7 +99,7 @@ LL | if let [0 | 1] = [0] {} | ~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:19:12 + --> $DIR/unnested_or_patterns.rs:22:12 | LL | if let [x, 0] | [x, 1] = [0, 1] {} | ^^^^^^^^^^^^^^^ @@ -110,7 +110,7 @@ LL | if let [x, 0 | 1] = [0, 1] {} | ~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:20:12 + --> $DIR/unnested_or_patterns.rs:23:12 | LL | if let [x, 0] | [x, 1] | [x, 2] = [0, 1] {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -121,7 +121,7 @@ LL | if let [x, 0 | 1 | 2] = [0, 1] {} | ~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:21:12 + --> $DIR/unnested_or_patterns.rs:24:12 | LL | if let [x, ..] | [x, 1] | [x, 2] = [0, 1] {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -132,7 +132,7 @@ LL | if let [x, ..] | [x, 1 | 2] = [0, 1] {} | ~~~~~~~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:23:12 + --> $DIR/unnested_or_patterns.rs:26:12 | LL | if let TS(0, x) | TS(1, x) = TS(0, 0) {} | ^^^^^^^^^^^^^^^^^^^ @@ -143,7 +143,7 @@ LL | if let TS(0 | 1, x) = TS(0, 0) {} | ~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:24:12 + --> $DIR/unnested_or_patterns.rs:27:12 | LL | if let TS(1, 0) | TS(2, 0) | TS(3, 0) = TS(0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -154,7 +154,7 @@ LL | if let TS(1 | 2 | 3, 0) = TS(0, 0) {} | ~~~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:25:12 + --> $DIR/unnested_or_patterns.rs:28:12 | LL | if let TS(x, ..) | TS(x, 1) | TS(x, 2) = TS(0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -165,7 +165,7 @@ LL | if let TS(x, ..) | TS(x, 1 | 2) = TS(0, 0) {} | ~~~~~~~~~~~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:30:12 + --> $DIR/unnested_or_patterns.rs:33:12 | LL | if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/useless_attribute.fixed b/tests/ui/useless_attribute.fixed index ce58a80347b..c23231a99e9 100644 --- a/tests/ui/useless_attribute.fixed +++ b/tests/ui/useless_attribute.fixed @@ -57,6 +57,12 @@ pub use std::io::prelude::*; #[allow(clippy::enum_glob_use)] pub use std::cmp::Ordering::*; +// don't lint on clippy::redundant_pub_crate +mod c { + #[allow(clippy::redundant_pub_crate)] + pub(crate) struct S; +} + fn test_indented_attr() { #![allow(clippy::almost_swapped)] use std::collections::HashSet; diff --git a/tests/ui/useless_attribute.rs b/tests/ui/useless_attribute.rs index c82bb9ba07f..7a7b198ea60 100644 --- a/tests/ui/useless_attribute.rs +++ b/tests/ui/useless_attribute.rs @@ -57,6 +57,12 @@ pub use std::io::prelude::*; #[allow(clippy::enum_glob_use)] pub use std::cmp::Ordering::*; +// don't lint on clippy::redundant_pub_crate +mod c { + #[allow(clippy::redundant_pub_crate)] + pub(crate) struct S; +} + fn test_indented_attr() { #[allow(clippy::almost_swapped)] use std::collections::HashSet; diff --git a/tests/ui/useless_attribute.stderr b/tests/ui/useless_attribute.stderr index d0194e4bbbe..255d2876355 100644 --- a/tests/ui/useless_attribute.stderr +++ b/tests/ui/useless_attribute.stderr @@ -13,7 +13,7 @@ LL | #[cfg_attr(feature = "cargo-clippy", allow(dead_code))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![cfg_attr(feature = "cargo-clippy", allow(dead_code)` error: useless lint attribute - --> $DIR/useless_attribute.rs:61:5 + --> $DIR/useless_attribute.rs:67:5 | LL | #[allow(clippy::almost_swapped)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(clippy::almost_swapped)]` diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index 8402c33a4cd..b6f47ae906b 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -8,8 +8,7 @@ // FIXME: We should likely add another edition 2021 test case for this lint #![warn(clippy::wildcard_imports)] -#![allow(unused)] -#![allow(clippy::unnecessary_wraps)] +#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)] #![warn(unused_imports)] extern crate wildcard_imports_helper; diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index faaeaade9b0..eb404b7a3de 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -8,8 +8,7 @@ // FIXME: We should likely add another edition 2021 test case for this lint #![warn(clippy::wildcard_imports)] -#![allow(unused)] -#![allow(clippy::unnecessary_wraps)] +#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)] #![warn(unused_imports)] extern crate wildcard_imports_helper; diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index 7534a65ec9b..626c1754fc8 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -1,5 +1,5 @@ error: usage of wildcard import - --> $DIR/wildcard_imports.rs:17:5 + --> $DIR/wildcard_imports.rs:16:5 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` @@ -7,85 +7,85 @@ LL | use crate::fn_mod::*; = note: `-D clippy::wildcard-imports` implied by `-D warnings` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:18:5 + --> $DIR/wildcard_imports.rs:17:5 | LL | use crate::mod_mod::*; | ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:19:5 + --> $DIR/wildcard_imports.rs:18:5 | LL | use crate::multi_fn_mod::*; | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:21:5 + --> $DIR/wildcard_imports.rs:20:5 | LL | use crate::struct_mod::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:25:5 + --> $DIR/wildcard_imports.rs:24:5 | LL | use wildcard_imports_helper::inner::inner_for_self_import::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:26:5 + --> $DIR/wildcard_imports.rs:25:5 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:97:13 + --> $DIR/wildcard_imports.rs:96:13 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:103:75 + --> $DIR/wildcard_imports.rs:102:75 | LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; | ^ help: try: `inner_extern_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:104:13 + --> $DIR/wildcard_imports.rs:103:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:115:20 + --> $DIR/wildcard_imports.rs:114:20 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^ help: try: `inner::inner_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:115:30 + --> $DIR/wildcard_imports.rs:114:30 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^^ help: try: `inner2::inner_bar` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:122:13 + --> $DIR/wildcard_imports.rs:121:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:151:9 + --> $DIR/wildcard_imports.rs:150:9 | LL | use crate::in_fn_test::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:160:9 + --> $DIR/wildcard_imports.rs:159:9 | LL | use crate:: in_fn_test:: * ; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:161:9 + --> $DIR/wildcard_imports.rs:160:9 | LL | use crate:: fn_mod:: | _________^ @@ -93,37 +93,37 @@ LL | | *; | |_________^ help: try: `crate:: fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:172:13 + --> $DIR/wildcard_imports.rs:171:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:207:17 + --> $DIR/wildcard_imports.rs:206:17 | LL | use super::*; | ^^^^^^^^ help: try: `super::insidefoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:215:13 + --> $DIR/wildcard_imports.rs:214:13 | LL | use super_imports::*; | ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:224:17 + --> $DIR/wildcard_imports.rs:223:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:233:13 + --> $DIR/wildcard_imports.rs:232:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:241:13 + --> $DIR/wildcard_imports.rs:240:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` diff --git a/tests/ui/wrong_self_convention.rs b/tests/ui/wrong_self_convention.rs index f8fee4b3ab2..e3cc90ee222 100644 --- a/tests/ui/wrong_self_convention.rs +++ b/tests/ui/wrong_self_convention.rs @@ -193,11 +193,6 @@ pub mod issue8142 { struct S; impl S { - // Should lint: is_ methods should only take &self, or no self at all. - fn is_still_buggy(&mut self) -> bool { - false - } - // Should not lint: "no self at all" is allowed. fn is_forty_two(x: u32) -> bool { x == 42 diff --git a/tests/ui/wrong_self_convention.stderr b/tests/ui/wrong_self_convention.stderr index 5493a99572e..2e7ee51d7e1 100644 --- a/tests/ui/wrong_self_convention.stderr +++ b/tests/ui/wrong_self_convention.stderr @@ -31,7 +31,7 @@ LL | fn into_i32(&self) {} | = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take `self` by reference or no `self` +error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self` --> $DIR/wrong_self_convention.rs:38:15 | LL | fn is_i32(self) {} @@ -71,7 +71,7 @@ LL | pub fn into_i64(&self) {} | = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take `self` by reference or no `self` +error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self` --> $DIR/wrong_self_convention.rs:46:19 | LL | pub fn is_i64(self) {} @@ -111,7 +111,7 @@ LL | fn into_i32_ref(&self) {} | = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take `self` by reference or no `self` +error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self` --> $DIR/wrong_self_convention.rs:98:19 | LL | fn is_i32(self) {} @@ -143,7 +143,7 @@ LL | fn into_i32_ref(&self); | = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take `self` by reference or no `self` +error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self` --> $DIR/wrong_self_convention.rs:122:19 | LL | fn is_i32(self); @@ -191,13 +191,5 @@ LL | fn to_u64(self) -> u64 { | = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take `self` by reference or no `self` - --> $DIR/wrong_self_convention.rs:197:27 - | -LL | fn is_still_buggy(&mut self) -> bool { - | ^^^^^^^^^ - | - = help: consider choosing a less ambiguous name - -error: aborting due to 25 previous errors +error: aborting due to 24 previous errors diff --git a/tests/ui/wrong_self_convention2.rs b/tests/ui/wrong_self_convention2.rs index a8fe8331133..0dcf4743e8b 100644 --- a/tests/ui/wrong_self_convention2.rs +++ b/tests/ui/wrong_self_convention2.rs @@ -104,3 +104,13 @@ mod issue4546 { pub fn to_other_thingy(self: Pin<&Self>) {} } } + +mod issue_8480_8513 { + struct Cat(String); + + impl Cat { + fn is_animal(&mut self) -> bool { + todo!(); + } + } +} From ed8458f67a56b0f01dcdd693339c1aa2ee7fa6ff Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 5 May 2022 15:20:07 +0100 Subject: [PATCH 03/19] (Partially) Revert "HACK: Move buggy lints to nursery" This reverts commit bb01aca86f66954b80798213711e8eadc4b26902. Partial: Keep regression tests --- clippy_lints/src/lib.register_nursery.rs | 2 -- clippy_lints/src/lib.register_pedantic.rs | 2 ++ clippy_lints/src/trait_bounds.rs | 4 ++-- tests/ui/trait_duplication_in_bounds.stderr | 10 +--------- tests/ui/type_repetition_in_bounds.stderr | 10 +--------- 5 files changed, 6 insertions(+), 22 deletions(-) diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index ec187563b3f..18904a94538 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -28,8 +28,6 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(strings::STRING_LIT_AS_BYTES), LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY), - LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), - LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR), LintId::of(transmute::USELESS_TRANSMUTE), LintId::of(use_self::USE_SELF), diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs index 2ee2c6e3358..63232fd4113 100644 --- a/clippy_lints/src/lib.register_pedantic.rs +++ b/clippy_lints/src/lib.register_pedantic.rs @@ -84,6 +84,8 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED), LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE), 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), diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 78e388a49af..c0aca2d517c 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -35,7 +35,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.38.0"] pub TYPE_REPETITION_IN_BOUNDS, - nursery, + pedantic, "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`" } @@ -65,7 +65,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.47.0"] pub TRAIT_DUPLICATION_IN_BOUNDS, - nursery, + pedantic, "Check if the same trait bounds are specified twice during a function declaration" } diff --git a/tests/ui/trait_duplication_in_bounds.stderr b/tests/ui/trait_duplication_in_bounds.stderr index d0a4cfb8837..6f8c8e47dfb 100644 --- a/tests/ui/trait_duplication_in_bounds.stderr +++ b/tests/ui/trait_duplication_in_bounds.stderr @@ -67,13 +67,5 @@ LL | Self: Iterator, | = help: consider removing this trait bound -error: this trait bound is already specified in the where clause - --> $DIR/trait_duplication_in_bounds.rs:99:23 - | -LL | fn impl_trait(_: impl AsRef, _: impl AsRef) {} - | ^^^^^^^^^^ - | - = help: consider removing this trait bound - -error: aborting due to 9 previous errors +error: aborting due to 8 previous errors diff --git a/tests/ui/type_repetition_in_bounds.stderr b/tests/ui/type_repetition_in_bounds.stderr index abc25e59496..148c19c7d07 100644 --- a/tests/ui/type_repetition_in_bounds.stderr +++ b/tests/ui/type_repetition_in_bounds.stderr @@ -19,13 +19,5 @@ LL | Self: Copy + Default + Ord, | = help: consider combining the bounds: `Self: Clone + Copy + Default + Ord` -error: this type has already been used as a bound predicate - --> $DIR/type_repetition_in_bounds.rs:83:43 - | -LL | fn impl_trait(_: impl AsRef, _: impl AsRef) {} - | ^^^^^^^^^^ - | - = help: consider combining the bounds: `impl AsRef: AsRef + AsRef` - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors From c09778dd21771eee53c32f53a570c53757d46ca2 Mon Sep 17 00:00:00 2001 From: Miguel Guarniz Date: Fri, 29 Apr 2022 13:11:22 -0400 Subject: [PATCH 04/19] use def_span and def_kind queries instead of calling tcx.hir() methods Signed-off-by: Miguel Guarniz --- clippy_lints/src/same_name_method.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/same_name_method.rs b/clippy_lints/src/same_name_method.rs index f63925a2f14..c5c174cc8f6 100644 --- a/clippy_lints/src/same_name_method.rs +++ b/clippy_lints/src/same_name_method.rs @@ -51,14 +51,14 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { let mut map = FxHashMap::::default(); for id in cx.tcx.hir().items() { - if matches!(cx.tcx.hir().def_kind(id.def_id), DefKind::Impl) + if matches!(cx.tcx.def_kind(id.def_id), DefKind::Impl) && let item = cx.tcx.hir().item(id) && let ItemKind::Impl(Impl { - items, - of_trait, - self_ty, - .. - }) = &item.kind + items, + of_trait, + self_ty, + .. + }) = &item.kind && let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind { if !map.contains_key(res) { From 41c7e4d3822c1a4d3d93fe64cb2d70d13d0687b5 Mon Sep 17 00:00:00 2001 From: Preston From Date: Fri, 18 Feb 2022 00:02:22 -0600 Subject: [PATCH 05/19] Lint for significant drops who may have surprising lifetimes #1 author Preston From 1645164142 -0600 committer Preston From 1650005351 -0600 --- CHANGELOG.md | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_nursery.rs | 1 + clippy_lints/src/lib.rs | 2 + .../src/significant_drop_in_scrutinee.rs | 408 ++++++++++++++ clippy_utils/src/attrs.rs | 1 + tests/ui/significant_drop_in_scrutinee.rs | 526 ++++++++++++++++++ tests/ui/significant_drop_in_scrutinee.stderr | 261 +++++++++ 8 files changed, 1201 insertions(+) create mode 100644 clippy_lints/src/significant_drop_in_scrutinee.rs create mode 100644 tests/ui/significant_drop_in_scrutinee.rs create mode 100644 tests/ui/significant_drop_in_scrutinee.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 751f9fccd88..d25ad0ac6fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3696,6 +3696,7 @@ Released 2018-09-13 [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next [`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization [`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive +[`significant_drop_in_scrutinee`]: https://rust-lang.github.io/rust-clippy/master/index.html#significant_drop_in_scrutinee [`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string [`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add [`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 5768edc5018..c888a5feda2 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -475,6 +475,7 @@ store.register_lints(&[ size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, stable_sort_primitive::STABLE_SORT_PRIMITIVE, + significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE, strings::STRING_ADD, strings::STRING_ADD_ASSIGN, strings::STRING_FROM_UTF8_AS_BYTES, diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index ec187563b3f..d43c7e03533 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -25,6 +25,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE), LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE), LintId::of(regex::TRIVIAL_REGEX), + LintId::of(significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE), LintId::of(strings::STRING_LIT_AS_BYTES), LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3bb821a1482..09071a255c5 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -364,6 +364,7 @@ mod self_named_constructors; mod semicolon_if_nothing_returned; mod serde_api; mod shadow; +mod significant_drop_in_scrutinee; mod single_char_lifetime_names; mod single_component_path_imports; mod size_of_in_element_count; @@ -874,6 +875,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv))); store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation)); store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion)); + store.register_late_pass(|| Box::new(significant_drop_in_scrutinee::SignificantDropInScrutinee)); store.register_late_pass(|| Box::new(dbg_macro::DbgMacro)); let cargo_ignore_publish = conf.cargo_ignore_publish; store.register_late_pass(move || { diff --git a/clippy_lints/src/significant_drop_in_scrutinee.rs b/clippy_lints/src/significant_drop_in_scrutinee.rs new file mode 100644 index 00000000000..94ae0c8f5a6 --- /dev/null +++ b/clippy_lints/src/significant_drop_in_scrutinee.rs @@ -0,0 +1,408 @@ +use crate::FxHashSet; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::get_attr; +use clippy_utils::source::{indent_of, snippet}; +use rustc_errors::{Applicability, Diagnostic}; +use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::{Ty, TypeAndMut}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// Check for temporaries returned from function calls in a match scrutinee that have the + /// `clippy::has_significant_drop` attribute. + /// + /// ### Why is this bad? + /// The `clippy::has_significant_drop` attribute can be added to types whose Drop impls have + /// an important side-effect, such as unlocking a mutex, making it important for users to be + /// able to accurately understand their lifetimes. When a temporary is returned in a function + /// call in a match scrutinee, its lifetime lasts until the end of the match block, which may + /// be surprising. + /// + /// For `Mutex`es this can lead to a deadlock. This happens when the match scrutinee uses a + /// function call that returns a `MutexGuard` and then tries to lock again in one of the match + /// arms. In that case the `MutexGuard` in the scrutinee will not be dropped until the end of + /// the match block and thus will not unlock. + /// + /// ### Example + /// ```rust + /// # use std::sync::Mutex; + /// + /// # struct State {} + /// + /// # impl State { + /// # fn foo(&self) -> bool { + /// # true + /// # } + /// + /// # fn bar(&self) {} + /// # } + /// + /// + /// let mutex = Mutex::new(State {}); + /// + /// match mutex.lock().unwrap().foo() { + /// true => { + /// mutex.lock().unwrap().bar(); // Deadlock! + /// } + /// false => {} + /// }; + /// + /// println!("All done!"); + /// + /// ``` + /// Use instead: + /// ```rust + /// # use std::sync::Mutex; + /// + /// # struct State {} + /// + /// # impl State { + /// # fn foo(&self) -> bool { + /// # true + /// # } + /// + /// # fn bar(&self) {} + /// # } + /// + /// let mutex = Mutex::new(State {}); + /// + /// let is_foo = mutex.lock().unwrap().foo(); + /// match is_foo { + /// true => { + /// mutex.lock().unwrap().bar(); + /// } + /// false => {} + /// }; + /// + /// println!("All done!"); + /// ``` + #[clippy::version = "1.60.0"] + pub SIGNIFICANT_DROP_IN_SCRUTINEE, + nursery, + "warns when a temporary of a type with a drop with a significant side-effect might have a surprising lifetime" +} + +declare_lint_pass!(SignificantDropInScrutinee => [SIGNIFICANT_DROP_IN_SCRUTINEE]); + +impl<'tcx> LateLintPass<'tcx> for SignificantDropInScrutinee { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let Some(suggestions) = has_significant_drop_in_scrutinee(cx, expr) { + for found in suggestions { + span_lint_and_then( + cx, + SIGNIFICANT_DROP_IN_SCRUTINEE, + found.found_span, + "temporary with significant drop in match scrutinee", + |diag| set_diagnostic(diag, cx, expr, found), + ) + } + } + } +} + +fn set_diagnostic<'tcx>(diag: &mut Diagnostic, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, found: FoundSigDrop) { + if found.lint_suggestion == LintSuggestion::MoveAndClone { + // If our suggestion is to move and clone, then we want to leave it to the user to + // decide how to address this lint, since it may be that cloning is inappropriate. + // Therefore, we won't to emit a suggestion. + return; + } + + let original = snippet(cx, found.found_span, ".."); + let trailing_indent = " ".repeat(indent_of(cx, found.found_span).unwrap_or(0)); + + let replacement = if found.lint_suggestion == LintSuggestion::MoveAndDerefToCopy { + format!("let value = *{};\n{}", original, trailing_indent) + } else if found.is_unit_return_val { + // If the return value of the expression to be moved is unit, then we don't need to + // capture the result in a temporary -- we can just replace it completely with `()`. + format!("{};\n{}", original, trailing_indent) + } else { + format!("let value = {};\n{}", original, trailing_indent) + }; + + let suggestion_message = if found.lint_suggestion == LintSuggestion::MoveOnly { + "try moving the temporary above the match" + } else { + "try moving the temporary above the match and create a copy" + }; + + let scrutinee_replacement = if found.is_unit_return_val { + "()".to_owned() + } else { + "value".to_owned() + }; + + diag.multipart_suggestion( + suggestion_message, + vec![ + (expr.span.shrink_to_lo(), replacement), + (found.found_span, scrutinee_replacement), + ], + Applicability::MaybeIncorrect, + ); +} + +/// If the expression is an ExprKind::Match, check if the scrutinee has a significant drop that may +/// have a surprising lifetime. +fn has_significant_drop_in_scrutinee<'tcx, 'a>( + cx: &'a LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, +) -> Option> { + let mut helper = SigDropHelper::new(cx); + match expr.kind { + ExprKind::Match(match_expr, _, _) => helper.find_sig_drop(match_expr), + _ => None, + } +} + +struct SigDropHelper<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + is_chain_end: bool, + seen_types: FxHashSet>, + has_significant_drop: bool, + current_sig_drop: Option, + sig_drop_spans: Option>, + special_handling_for_binary_op: bool, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +enum LintSuggestion { + MoveOnly, + MoveAndDerefToCopy, + MoveAndClone, +} + +#[derive(Clone, Copy)] +struct FoundSigDrop { + found_span: Span, + is_unit_return_val: bool, + lint_suggestion: LintSuggestion, +} + +impl<'a, 'tcx> SigDropHelper<'a, 'tcx> { + fn new(cx: &'a LateContext<'tcx>) -> SigDropHelper<'a, 'tcx> { + SigDropHelper { + cx, + is_chain_end: true, + seen_types: FxHashSet::default(), + has_significant_drop: false, + current_sig_drop: None, + sig_drop_spans: None, + special_handling_for_binary_op: false, + } + } + + fn find_sig_drop(&mut self, match_expr: &'tcx Expr<'_>) -> Option> { + self.visit_expr(match_expr); + + // If sig drop spans is empty but we found a significant drop, it means that we didn't find + // a type that was trivially copyable as we moved up the chain after finding a significant + // drop, so move the entire scrutinee. + if self.has_significant_drop && self.sig_drop_spans.is_none() { + self.try_setting_current_suggestion(match_expr, true); + self.move_current_suggestion(); + } + + self.sig_drop_spans.take() + } + + /// This will try to set the current suggestion (so it can be moved into the suggestions vec + /// later). If allow_move_and_clone is false, the suggestion *won't* be set -- this gives us + /// an opportunity to look for another type in the chain that will be trivially copyable. + /// However, if we are at the the end of the chain, we want to accept whatever is there. (The + /// suggestion won't actually be output, but the diagnostic message will be output, so the user + /// can determine the best way to handle the lint.) + fn try_setting_current_suggestion(&mut self, expr: &'tcx Expr<'_>, allow_move_and_clone: bool) { + if self.current_sig_drop.is_some() { + return; + } + let ty = self.get_type(expr); + if ty.is_ref() { + // We checked that the type was ref, so builtin_deref will return Some TypeAndMut, + // but let's avoid any chance of an ICE + if let Some(TypeAndMut { ty, .. }) = ty.builtin_deref(true) { + if ty.is_trivially_pure_clone_copy() { + self.current_sig_drop.replace(FoundSigDrop { + found_span: expr.span, + is_unit_return_val: false, + lint_suggestion: LintSuggestion::MoveAndDerefToCopy, + }); + } else if allow_move_and_clone { + self.current_sig_drop.replace(FoundSigDrop { + found_span: expr.span, + is_unit_return_val: false, + lint_suggestion: LintSuggestion::MoveAndClone, + }); + } + } + } else if ty.is_trivially_pure_clone_copy() { + self.current_sig_drop.replace(FoundSigDrop { + found_span: expr.span, + is_unit_return_val: false, + lint_suggestion: LintSuggestion::MoveOnly, + }); + } + } + + fn move_current_suggestion(&mut self) { + if let Some(current) = self.current_sig_drop.take() { + self.sig_drop_spans.get_or_insert_with(Vec::new).push(current); + } + } + + fn get_type(&self, ex: &'tcx Expr<'_>) -> Ty<'tcx> { + self.cx.typeck_results().expr_ty(ex) + } + + fn has_seen_type(&mut self, ty: Ty<'tcx>) -> bool { + !self.seen_types.insert(ty) + } + + fn visit_exprs_for_binary_ops( + &mut self, + left: &'tcx Expr<'_>, + right: &'tcx Expr<'_>, + is_unit_return_val: bool, + span: Span, + ) { + self.special_handling_for_binary_op = true; + self.visit_expr(left); + self.visit_expr(right); + + // If either side had a significant drop, suggest moving the entire scrutinee to avoid + // unnecessary copies and to simplify cases where both sides have significant drops. + if self.has_significant_drop { + self.current_sig_drop.replace(FoundSigDrop { + found_span: span, + is_unit_return_val, + lint_suggestion: LintSuggestion::MoveOnly, + }); + } + + self.special_handling_for_binary_op = false; + } + + fn has_sig_drop_attr(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + if let Some(adt) = ty.ty_adt_def() { + if get_attr(cx.sess(), cx.tcx.get_attrs(adt.did()), "has_significant_drop").count() > 0 { + return true; + } + } + + match ty.kind() { + rustc_middle::ty::Adt(a, b) => { + for f in a.all_fields() { + let ty = f.ty(cx.tcx, b); + if !self.has_seen_type(ty) && self.has_sig_drop_attr(cx, ty) { + return true; + } + } + + for generic_arg in b.iter() { + if let GenericArgKind::Type(ty) = generic_arg.unpack() { + if self.has_sig_drop_attr(cx, ty) { + return true; + } + } + } + false + }, + rustc_middle::ty::Array(ty, _) => self.has_sig_drop_attr(cx, *ty), + rustc_middle::ty::RawPtr(TypeAndMut { ty, .. }) => self.has_sig_drop_attr(cx, *ty), + rustc_middle::ty::Ref(_, ty, _) => self.has_sig_drop_attr(cx, *ty), + rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(cx, *ty), + _ => false, + } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> { + fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { + if !self.is_chain_end && self.has_sig_drop_attr(self.cx, self.get_type(ex)) { + self.has_significant_drop = true; + return; + } + self.is_chain_end = false; + + match ex.kind { + ExprKind::MethodCall(_, [ref expr, ..], _) => { + self.visit_expr(expr) + } + ExprKind::Binary(_, left, right) => { + self.visit_exprs_for_binary_ops(left, right, false, ex.span); + } + ExprKind::Assign(left, right, _) => { + self.visit_exprs_for_binary_ops(left, right, true, ex.span); + } + ExprKind::AssignOp(_, left, right) => { + self.visit_exprs_for_binary_ops(left, right, true, ex.span); + } + ExprKind::Tup(exprs) => { + for expr in exprs { + self.visit_expr(expr); + if self.has_significant_drop { + // We may have not have set current_sig_drop if all the suggestions were + // MoveAndClone, so add this tuple item's full expression in that case. + if self.current_sig_drop.is_none() { + self.try_setting_current_suggestion(expr, true); + } + + // Now we are guaranteed to have something, so add it to the final vec. + self.move_current_suggestion(); + } + // Reset `has_significant_drop` after each tuple expression so we can look for + // additional cases. + self.has_significant_drop = false; + } + if self.sig_drop_spans.is_some() { + self.has_significant_drop = true; + } + } + ExprKind::Box(..) | + ExprKind::Array(..) | + ExprKind::Call(..) | + ExprKind::Unary(..) | + ExprKind::If(..) | + ExprKind::Match(..) | + ExprKind::Field(..) | + ExprKind::Index(..) | + ExprKind::Ret(..) | + ExprKind::Repeat(..) | + ExprKind::Yield(..) | + ExprKind::MethodCall(..) => walk_expr(self, ex), + ExprKind::AddrOf(_, _, _) | + ExprKind::Block(_, _) | + ExprKind::Break(_, _) | + ExprKind::Cast(_, _) | + // Don't want to check the closure itself, only invocation, which is covered by MethodCall + ExprKind::Closure(_, _, _, _, _) | + ExprKind::ConstBlock(_) | + ExprKind::Continue(_) | + ExprKind::DropTemps(_) | + ExprKind::Err | + ExprKind::InlineAsm(_) | + ExprKind::Let(_) | + ExprKind::Lit(_) | + ExprKind::Loop(_, _, _, _) | + ExprKind::Path(_) | + ExprKind::Struct(_, _, _) | + ExprKind::Type(_, _) => { + return; + } + } + + // Once a significant temporary has been found, we need to go back up at least 1 level to + // find the span to extract for replacement, so the temporary gets dropped. However, for + // binary ops, we want to move the whole scrutinee so we avoid unnecessary copies and to + // simplify cases where both sides have significant drops. + if self.has_significant_drop && !self.special_handling_for_binary_op { + self.try_setting_current_suggestion(ex, false); + } + } +} diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index 25a84d16650..7f448175e32 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -22,6 +22,7 @@ pub const BUILTIN_ATTRIBUTES: &[(&str, DeprecationStatus)] = &[ ("cyclomatic_complexity", DeprecationStatus::Replaced("cognitive_complexity")), ("dump", DeprecationStatus::None), ("msrv", DeprecationStatus::None), + ("has_significant_drop", DeprecationStatus::None), ]; pub struct LimitStack { diff --git a/tests/ui/significant_drop_in_scrutinee.rs b/tests/ui/significant_drop_in_scrutinee.rs new file mode 100644 index 00000000000..c4a3301e722 --- /dev/null +++ b/tests/ui/significant_drop_in_scrutinee.rs @@ -0,0 +1,526 @@ +// FIXME: Ideally these suggestions would be fixed via rustfix. Blocked by rust-lang/rust#53934 +// // run-rustfix + +#![warn(clippy::significant_drop_in_scrutinee)] +#![allow(clippy::single_match)] +#![allow(clippy::match_single_binding)] +#![allow(unused_assignments)] +#![allow(dead_code)] + +use std::ops::Deref; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::{Mutex, MutexGuard}; + +struct State {} + +impl State { + fn foo(&self) -> bool { + true + } + + fn bar(&self) {} +} + +fn should_not_trigger_lint_with_mutex_guard_outside_match() { + let mutex = Mutex::new(State {}); + + // Should not trigger lint because the temporary should drop at the `;` on line before the match + let is_foo = mutex.lock().unwrap().foo(); + match is_foo { + true => { + mutex.lock().unwrap().bar(); + } + false => {} + }; +} + +fn should_not_trigger_lint_with_mutex_guard_when_taking_ownership_in_match() { + let mutex = Mutex::new(State {}); + + // Should not trigger lint because the scrutinee is explicitly returning the MutexGuard, + // so its lifetime should not be surprising. + match mutex.lock() { + Ok(guard) => { + guard.foo(); + mutex.lock().unwrap().bar(); + } + _ => {} + }; +} + +fn should_trigger_lint_with_mutex_guard_in_match_scrutinee() { + let mutex = Mutex::new(State {}); + + // Should trigger lint because the lifetime of the temporary MutexGuard is surprising because it + // is preserved until the end of the match, but there is no clear indication that this is the + // case. + match mutex.lock().unwrap().foo() { + true => { + mutex.lock().unwrap().bar(); + } + false => {} + }; +} + +fn should_not_trigger_lint_for_insignificant_drop() { + // Should not trigger lint because there are no temporaries whose drops have a significant + // side effect. + match 1u64.to_string().is_empty() { + true => { + println!("It was empty") + } + false => { + println!("It was not empty") + } + } +} + +struct StateWithMutex { + m: Mutex, +} + +struct MutexGuardWrapper<'a> { + mg: MutexGuard<'a, u64>, +} + +impl<'a> MutexGuardWrapper<'a> { + fn get_the_value(&self) -> u64 { + *self.mg.deref() + } +} + +struct MutexGuardWrapperWrapper<'a> { + mg: MutexGuardWrapper<'a>, +} + +impl<'a> MutexGuardWrapperWrapper<'a> { + fn get_the_value(&self) -> u64 { + *self.mg.mg.deref() + } +} + +impl StateWithMutex { + fn lock_m(&self) -> MutexGuardWrapper<'_> { + MutexGuardWrapper { + mg: self.m.lock().unwrap(), + } + } + + fn lock_m_m(&self) -> MutexGuardWrapperWrapper<'_> { + MutexGuardWrapperWrapper { + mg: MutexGuardWrapper { + mg: self.m.lock().unwrap(), + }, + } + } + + fn foo(&self) -> bool { + true + } + + fn bar(&self) {} +} + +fn should_trigger_lint_with_wrapped_mutex() { + let s = StateWithMutex { m: Mutex::new(1) }; + + // Should trigger lint because a temporary contains a type with a significant drop and its + // lifetime is not obvious. Additionally, it is not obvious from looking at the scrutinee that + // the temporary contains such a type, making it potentially even more surprising. + match s.lock_m().get_the_value() { + 1 => { + println!("Got 1. Is it still 1?"); + println!("{}", s.lock_m().get_the_value()); + } + 2 => { + println!("Got 2. Is it still 2?"); + println!("{}", s.lock_m().get_the_value()); + } + _ => {} + } + println!("All done!"); +} + +fn should_trigger_lint_with_double_wrapped_mutex() { + let s = StateWithMutex { m: Mutex::new(1) }; + + // Should trigger lint because a temporary contains a type which further contains a type with a + // significant drop and its lifetime is not obvious. Additionally, it is not obvious from + // looking at the scrutinee that the temporary contains such a type, making it potentially even + // more surprising. + match s.lock_m_m().get_the_value() { + 1 => { + println!("Got 1. Is it still 1?"); + println!("{}", s.lock_m().get_the_value()); + } + 2 => { + println!("Got 2. Is it still 2?"); + println!("{}", s.lock_m().get_the_value()); + } + _ => {} + } + println!("All done!"); +} + +struct Counter { + i: AtomicU64, +} + +#[clippy::has_significant_drop] +struct CounterWrapper<'a> { + counter: &'a Counter, +} + +impl<'a> CounterWrapper<'a> { + fn new(counter: &Counter) -> CounterWrapper { + counter.i.fetch_add(1, Ordering::Relaxed); + CounterWrapper { counter } + } +} + +impl<'a> Drop for CounterWrapper<'a> { + fn drop(&mut self) { + self.counter.i.fetch_sub(1, Ordering::Relaxed); + } +} + +impl Counter { + fn temp_increment(&self) -> Vec { + vec![CounterWrapper::new(self), CounterWrapper::new(self)] + } +} + +fn should_trigger_lint_for_vec() { + let counter = Counter { i: AtomicU64::new(0) }; + + // Should trigger lint because the temporary in the scrutinee returns a collection of types + // which have significant drops. The types with significant drops are also non-obvious when + // reading the expression in the scrutinee. + match counter.temp_increment().len() { + 2 => { + let current_count = counter.i.load(Ordering::Relaxed); + println!("Current count {}", current_count); + assert_eq!(current_count, 0); + } + 1 => {} + 3 => {} + _ => {} + }; +} + +struct StateWithField { + s: String, +} + +// Should trigger lint only on the type in the tuple which is created using a temporary +// with a significant drop. Additionally, this test ensures that the format of the tuple +// is preserved correctly in the suggestion. +fn should_trigger_lint_for_tuple_in_scrutinee() { + let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() }); + + { + match (mutex1.lock().unwrap().s.len(), true) { + (3, _) => { + println!("started"); + mutex1.lock().unwrap().s.len(); + println!("done"); + } + (_, _) => {} + }; + + match (true, mutex1.lock().unwrap().s.len(), true) { + (_, 3, _) => { + println!("started"); + mutex1.lock().unwrap().s.len(); + println!("done"); + } + (_, _, _) => {} + }; + + let mutex2 = Mutex::new(StateWithField { s: "two".to_owned() }); + match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) { + (3, _, 3) => { + println!("started"); + mutex1.lock().unwrap().s.len(); + mutex2.lock().unwrap().s.len(); + println!("done"); + } + (_, _, _) => {} + }; + + let mutex3 = Mutex::new(StateWithField { s: "three".to_owned() }); + match mutex3.lock().unwrap().s.as_str() { + "three" => { + println!("started"); + mutex1.lock().unwrap().s.len(); + mutex2.lock().unwrap().s.len(); + println!("done"); + } + _ => {} + }; + + + match (true, mutex3.lock().unwrap().s.as_str()) { + (_, "three") => { + println!("started"); + mutex1.lock().unwrap().s.len(); + mutex2.lock().unwrap().s.len(); + println!("done"); + } + (_, _) => {} + }; + } +} + +// Should trigger lint when either side of a binary operation creates a temporary with a +// significant drop. +// To avoid potential unnecessary copies or creating references that would trigger the significant +// drop problem, the lint recommends moving the entire binary operation. +fn should_trigger_lint_for_accessing_field_in_mutex_in_one_side_of_binary_op() { + let mutex = Mutex::new(StateWithField { s: "state".to_owned() }); + + match mutex.lock().unwrap().s.len() > 1 { + true => { + mutex.lock().unwrap().s.len(); + } + false => {} + }; + + match 1 < mutex.lock().unwrap().s.len() { + true => { + mutex.lock().unwrap().s.len(); + } + false => {} + }; +} + +// Should trigger lint when both sides of a binary operation creates a temporary with a +// significant drop. +// To avoid potential unnecessary copies or creating references that would trigger the significant +// drop problem, the lint recommends moving the entire binary operation. +fn should_trigger_lint_for_accessing_fields_in_mutex_in_both_sides_of_binary_op() { + let mutex1 = Mutex::new(StateWithField { s: "state".to_owned() }); + let mutex2 = Mutex::new(StateWithField { s: "statewithfield".to_owned() }); + + match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() { + true => { + println!("{} < {}", mutex1.lock().unwrap().s.len(), mutex2.lock().unwrap().s.len()); + } + false => {} + }; + + match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() { + true => { + println!("{} >= {}", mutex1.lock().unwrap().s.len(), mutex2.lock().unwrap().s.len()); + } + false => {} + }; +} + +fn should_not_trigger_lint_for_closure_in_scrutinee() { + let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() }); + + let get_mutex_guard = || mutex1.lock().unwrap().s.len(); + + // Should not trigger lint because the temporary with a significant drop will be dropped + // at the end of the closure, so the MutexGuard will be unlocked and not have a potentially + // surprising lifetime. + match get_mutex_guard() > 1 { + true => { + mutex1.lock().unwrap().s.len(); + } + false => {} + }; +} + +fn should_trigger_lint_for_return_from_closure_in_scrutinee() { + let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() }); + + let get_mutex_guard = || mutex1.lock().unwrap(); + + // Should trigger lint because the temporary with a significant drop is returned from the + // closure but not used directly in any match arms, so it has a potentially surprising lifetime. + match get_mutex_guard().s.len() > 1 { + true => { + mutex1.lock().unwrap().s.len(); + } + false => {} + }; +} + +fn should_trigger_lint_for_return_from_match_in_scrutinee() { + let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() }); + let mutex2 = Mutex::new(StateWithField { s: "two".to_owned() }); + + let i = 100; + + // Should trigger lint because the nested match within the scrutinee returns a temporary with a + // significant drop is but not used directly in any match arms, so it has a potentially + // surprising lifetime. + match match i { 100 => mutex1.lock().unwrap(), _ => mutex2.lock().unwrap() }.s.len() > 1 { + true => { + mutex1.lock().unwrap().s.len(); + } + false => { + println!("nothing to do here"); + } + }; +} + +fn should_trigger_lint_for_return_from_if_in_scrutinee() { + let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() }); + let mutex2 = Mutex::new(StateWithField { s: "two".to_owned() }); + + let i = 100; + + // Should trigger lint because the nested if-expression within the scrutinee returns a temporary + // with a significant drop is but not used directly in any match arms, so it has a potentially + // surprising lifetime. + match if i > 1 { mutex1.lock().unwrap() } else { mutex2.lock().unwrap() }.s.len() > 1 { + true => { + mutex1.lock().unwrap().s.len(); + } + false => {} + }; +} + +fn should_not_trigger_lint_for_if_in_scrutinee() { + let mutex = Mutex::new(StateWithField { s: "state".to_owned() }); + + let i = 100; + + // Should not trigger the lint because the temporary with a significant drop *is* dropped within + // the body of the if-expression nested within the match scrutinee, and therefore does not have + // a potentially surprising lifetime. + match if i > 1 { mutex.lock().unwrap().s.len() > 1 } else { false } { + true => { + mutex.lock().unwrap().s.len(); + } + false => {} + }; +} + +struct StateWithBoxedMutexGuard { + u: Mutex, +} + +impl StateWithBoxedMutexGuard { + fn new() -> StateWithBoxedMutexGuard { + StateWithBoxedMutexGuard { u: Mutex::new(42) } + } + fn lock(&self) -> Box> { + Box::new(self.u.lock().unwrap()) + } +} + +fn should_trigger_lint_for_boxed_mutex_guard() { + let s = StateWithBoxedMutexGuard::new(); + + // Should trigger lint because a temporary Box holding a type with a significant drop in a match + // scrutinee may have a potentially surprising lifetime. + match s.lock().deref().deref() { + 0 | 1 => println!("Value was less than 2"), + _ => println!("Value is {}", s.lock().deref()), + }; +} + +struct StateStringWithBoxedMutexGuard { + s: Mutex, +} + +impl StateStringWithBoxedMutexGuard { + fn new() -> StateStringWithBoxedMutexGuard { + StateStringWithBoxedMutexGuard { s: Mutex::new("A String".to_owned()) } + } + fn lock(&self) -> Box> { + Box::new(self.s.lock().unwrap()) + } +} + +fn should_trigger_lint_for_boxed_mutex_guard_holding_string() { + let s = StateStringWithBoxedMutexGuard::new(); + + let matcher = String::from("A String"); + + // Should trigger lint because a temporary Box holding a type with a significant drop in a match + // scrutinee may have a potentially surprising lifetime. + match s.lock().deref().deref() { + matcher => println!("Value is {}", s.lock().deref()), + _ => println!("Value was not a match"), + }; +} + + +struct StateWithIntField { + i: u64, +} + +// Should trigger lint when either side of an assign expression contains a temporary with a +// significant drop, because the temporary's lifetime will be extended to the end of the match. +// To avoid potential unnecessary copies or creating references that would trigger the significant +// drop problem, the lint recommends moving the entire binary operation. +fn should_trigger_lint_in_assign_expr() { + let mutex = Mutex::new(StateWithIntField { i: 10 }); + + let mut i = 100; + + match mutex.lock().unwrap().i = i { + _ => { + println!("{}", mutex.lock().unwrap().i); + } + }; + + match i = mutex.lock().unwrap().i { + _ => { + println!("{}", mutex.lock().unwrap().i); + } + }; + + match mutex.lock().unwrap().i += 1 { + _ => { + println!("{}", mutex.lock().unwrap().i); + } + }; + + match i += mutex.lock().unwrap().i { + _ => { + println!("{}", mutex.lock().unwrap().i); + } + }; +} + +#[derive(Debug)] +enum RecursiveEnum { + Foo(Option>) +} + +#[derive(Debug)] +enum GenericRecursiveEnum { + Foo(T, Option>>) +} + +fn should_not_cause_stack_overflow() { + // Test that when a type recursively contains itself, a stack overflow does not occur when + // checking sub-types for significant drops. + let f = RecursiveEnum::Foo(Some(Box::new(RecursiveEnum::Foo(None)))); + match f { + RecursiveEnum::Foo(Some(f)) => { + println!("{:?}", f) + } + RecursiveEnum::Foo(f) => { + println!("{:?}", f) + } + } + + let f = GenericRecursiveEnum::Foo(1u64, Some(Box::new(GenericRecursiveEnum::Foo(2u64, None)))); + match f { + GenericRecursiveEnum::Foo(i, Some(f)) => { + println!("{} {:?}", i, f) + } + GenericRecursiveEnum::Foo(i, f) => { + println!("{} {:?}", i, f) + } + } +} + +fn main() {} diff --git a/tests/ui/significant_drop_in_scrutinee.stderr b/tests/ui/significant_drop_in_scrutinee.stderr new file mode 100644 index 00000000000..c442e93f539 --- /dev/null +++ b/tests/ui/significant_drop_in_scrutinee.stderr @@ -0,0 +1,261 @@ +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:57:11 + | +LL | match mutex.lock().unwrap().foo() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::significant-drop-in-scrutinee` implied by `-D warnings` +help: try moving the temporary above the match + | +LL ~ let value = mutex.lock().unwrap().foo(); +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:130:11 + | +LL | match s.lock_m().get_the_value() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = s.lock_m().get_the_value(); +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:151:11 + | +LL | match s.lock_m_m().get_the_value() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = s.lock_m_m().get_the_value(); +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:199:11 + | +LL | match counter.temp_increment().len() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = counter.temp_increment().len(); +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:222:16 + | +LL | match (mutex1.lock().unwrap().s.len(), true) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = mutex1.lock().unwrap().s.len(); +LL ~ match (value, true) { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:231:22 + | +LL | match (true, mutex1.lock().unwrap().s.len(), true) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = mutex1.lock().unwrap().s.len(); +LL ~ match (true, value, true) { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:241:16 + | +LL | match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = mutex1.lock().unwrap().s.len(); +LL ~ match (value, true, mutex2.lock().unwrap().s.len()) { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:241:54 + | +LL | match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = mutex2.lock().unwrap().s.len(); +LL ~ match (mutex1.lock().unwrap().s.len(), true, value) { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:252:15 + | +LL | match mutex3.lock().unwrap().s.as_str() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:263:22 + | +LL | match (true, mutex3.lock().unwrap().s.as_str()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:282:11 + | +LL | match mutex.lock().unwrap().s.len() > 1 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = mutex.lock().unwrap().s.len() > 1; +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:289:11 + | +LL | match 1 < mutex.lock().unwrap().s.len() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = 1 < mutex.lock().unwrap().s.len(); +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:305:11 + | +LL | match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len(); +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:312:11 + | +LL | match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len(); +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:343:11 + | +LL | match get_mutex_guard().s.len() > 1 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = get_mutex_guard().s.len() > 1; +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:360:11 + | +LL | match match i { 100 => mutex1.lock().unwrap(), _ => mutex2.lock().unwrap() }.s.len() > 1 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = match i { 100 => mutex1.lock().unwrap(), _ => mutex2.lock().unwrap() }.s.len() > 1; +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:379:11 + | +LL | match if i > 1 { mutex1.lock().unwrap() } else { mutex2.lock().unwrap() }.s.len() > 1 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = if i > 1 { mutex1.lock().unwrap() } else { mutex2.lock().unwrap() }.s.len() > 1; +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:421:11 + | +LL | match s.lock().deref().deref() { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match and create a copy + | +LL ~ let value = *s.lock().deref().deref(); +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:447:11 + | +LL | match s.lock().deref().deref() { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:467:11 + | +LL | match mutex.lock().unwrap().i = i { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ mutex.lock().unwrap().i = i; +LL ~ match () { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:473:11 + | +LL | match i = mutex.lock().unwrap().i { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ i = mutex.lock().unwrap().i; +LL ~ match () { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:479:11 + | +LL | match mutex.lock().unwrap().i += 1 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ mutex.lock().unwrap().i += 1; +LL ~ match () { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:485:11 + | +LL | match i += mutex.lock().unwrap().i { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ i += mutex.lock().unwrap().i; +LL ~ match () { + | + +error: aborting due to 23 previous errors + From bca3d8a6b5f04fe2a6ca95ff22d07ee73ad80604 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 5 May 2022 15:50:11 +0100 Subject: [PATCH 06/19] Track if a where bound comes from a impl Trait desugar With #93803 `impl Trait` function arguments get desugared to hidden where bounds. However, Clippy needs to know if a bound was originally a impl Trait or an actual bound. This adds a field to the `WhereBoundPredicate` struct to keep track of this information during HIR lowering. --- clippy_lints/src/lifetimes.rs | 6 +++--- clippy_lints/src/trait_bounds.rs | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index ab5d3fa7b6d..51d5b510ab9 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -9,8 +9,8 @@ use rustc_hir::intravisit::{ use rustc_hir::FnRetTy::Return; use rustc_hir::{ BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem, - ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, TraitBoundModifier, - TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, + ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, PredicateOrigin, + TraitBoundModifier, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter as middle_nested_filter; @@ -145,7 +145,7 @@ fn check_fn_inner<'tcx>( .filter(|param| matches!(param.kind, GenericParamKind::Type { .. })); for typ in types { for pred in generics.bounds_for_param(cx.tcx.hir().local_def_id(typ.hir_id)) { - if pred.in_where_clause { + if pred.origin == PredicateOrigin::WhereClause { // has_where_lifetimes checked that this predicate contains no lifetime. continue; } diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index c0aca2d517c..911da3997ae 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -8,7 +8,8 @@ use rustc_data_structures::unhash::UnhashMap; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{ - GenericBound, Generics, Item, ItemKind, Node, Path, PathSegment, QPath, TraitItem, Ty, TyKind, WherePredicate, + GenericBound, Generics, Item, ItemKind, Node, Path, PathSegment, PredicateOrigin, QPath, TraitItem, Ty, TyKind, + WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -95,6 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { for predicate in item.generics.predicates { if_chain! { if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate; + if bound_predicate.origin != PredicateOrigin::ImplTrait; if !bound_predicate.span.from_expansion(); if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind; if let Some(PathSegment { @@ -168,6 +170,7 @@ impl TraitBounds { for bound in gen.predicates { if_chain! { if let WherePredicate::BoundPredicate(ref p) = bound; + if p.origin != PredicateOrigin::ImplTrait; if p.bounds.len() as u64 <= self.max_trait_bounds; if !p.span.from_expansion(); if let Some(ref v) = map.insert( @@ -223,6 +226,7 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { for predicate in gen.predicates { if_chain! { if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate; + if bound_predicate.origin != PredicateOrigin::ImplTrait; if !bound_predicate.span.from_expansion(); if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind; if let Some(segment) = segments.first(); From 21e4765e58b0f92bc266c5370ecec270c0b2206c Mon Sep 17 00:00:00 2001 From: xFrednet Date: Thu, 31 Mar 2022 21:49:50 +0200 Subject: [PATCH 07/19] Test `expect` attribute for tool lints, clippy edition (RFC 2383) --- tests/ui/expect_tool_lint_rfc_2383.rs | 142 ++++++++++++++++++++++ tests/ui/expect_tool_lint_rfc_2383.stderr | 40 ++++++ 2 files changed, 182 insertions(+) create mode 100644 tests/ui/expect_tool_lint_rfc_2383.rs create mode 100644 tests/ui/expect_tool_lint_rfc_2383.stderr diff --git a/tests/ui/expect_tool_lint_rfc_2383.rs b/tests/ui/expect_tool_lint_rfc_2383.rs new file mode 100644 index 00000000000..28b37f96e91 --- /dev/null +++ b/tests/ui/expect_tool_lint_rfc_2383.rs @@ -0,0 +1,142 @@ +// check-pass +#![feature(lint_reasons)] +//! This file tests the `#[expect]` attribute implementation for tool lints. The same +//! file is used to test clippy and rustdoc. Any changes to this file should be synced +//! to the other test files as well. +//! +//! Expectations: +//! * rustc: only rustc lint expectations are emitted +//! * clippy: rustc and Clippy's expectations are emitted +//! * rustdoc: only rustdoc lint expectations are emitted +//! +//! This test can't cover every lint from Clippy, rustdoc and potentially other +//! tools that will be developed. This therefore only tests a small subset of lints +#![expect(rustdoc::missing_crate_level_docs)] + +mod rustc_ok { + //! See + + #[expect(dead_code)] + pub fn rustc_lints() { + let x = 42.0; + + #[expect(illegal_floating_point_literal_pattern)] + match x { + 5.0 => {} + 6.0 => {} + _ => {} + } + } +} + +mod rustc_warn { + //! See + + #[expect(dead_code)] + pub fn rustc_lints() { + let x = 42; + + #[expect(illegal_floating_point_literal_pattern)] + match x { + 5 => {} + 6 => {} + _ => {} + } + } +} + +pub mod rustdoc_ok { + //! See + + #[expect(rustdoc::broken_intra_doc_links)] + /// I want to link to [`Nonexistent`] but it doesn't exist! + pub fn foo() {} + + #[expect(rustdoc::invalid_html_tags)] + ///

+ pub fn bar() {} + + #[expect(rustdoc::bare_urls)] + /// http://example.org + pub fn baz() {} +} + +pub mod rustdoc_warn { + //! See + + #[expect(rustdoc::broken_intra_doc_links)] + /// I want to link to [`bar`] but it doesn't exist! + pub fn foo() {} + + #[expect(rustdoc::invalid_html_tags)] + ///

+ pub fn bar() {} + + #[expect(rustdoc::bare_urls)] + /// + pub fn baz() {} +} + +mod clippy_ok { + //! See + + #[expect(clippy::almost_swapped)] + fn foo() { + let mut a = 0; + let mut b = 9; + a = b; + b = a; + } + + #[expect(clippy::bytes_nth)] + fn bar() { + let _ = "Hello".bytes().nth(3); + } + + #[expect(clippy::if_same_then_else)] + fn baz() { + let _ = if true { 42 } else { 42 }; + } + + #[expect(clippy::logic_bug)] + fn burger() { + let a = false; + let b = true; + + if a && b || a {} + } +} + +mod clippy_warn { + //! See + + #[expect(clippy::almost_swapped)] + fn foo() { + let mut a = 0; + let mut b = 9; + a = b; + } + + #[expect(clippy::bytes_nth)] + fn bar() { + let _ = "Hello".as_bytes().get(3); + } + + #[expect(clippy::if_same_then_else)] + fn baz() { + let _ = if true { 33 } else { 42 }; + } + + #[expect(clippy::logic_bug)] + fn burger() { + let a = false; + let b = true; + let c = false; + + if a && b || c {} + } +} + +fn main() { + rustc_warn::rustc_lints(); +} diff --git a/tests/ui/expect_tool_lint_rfc_2383.stderr b/tests/ui/expect_tool_lint_rfc_2383.stderr new file mode 100644 index 00000000000..db29e85a821 --- /dev/null +++ b/tests/ui/expect_tool_lint_rfc_2383.stderr @@ -0,0 +1,40 @@ +error: this lint expectation is unfulfilled + --> $DIR/expect_tool_lint_rfc_2383.rs:35:14 + | +LL | #[expect(dead_code)] + | ^^^^^^^^^ + | + = note: `-D unfulfilled-lint-expectations` implied by `-D warnings` + +error: this lint expectation is unfulfilled + --> $DIR/expect_tool_lint_rfc_2383.rs:39:18 + | +LL | #[expect(illegal_floating_point_literal_pattern)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this lint expectation is unfulfilled + --> $DIR/expect_tool_lint_rfc_2383.rs:113:14 + | +LL | #[expect(clippy::almost_swapped)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: this lint expectation is unfulfilled + --> $DIR/expect_tool_lint_rfc_2383.rs:120:14 + | +LL | #[expect(clippy::bytes_nth)] + | ^^^^^^^^^^^^^^^^^ + +error: this lint expectation is unfulfilled + --> $DIR/expect_tool_lint_rfc_2383.rs:125:14 + | +LL | #[expect(clippy::if_same_then_else)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this lint expectation is unfulfilled + --> $DIR/expect_tool_lint_rfc_2383.rs:130:14 + | +LL | #[expect(clippy::logic_bug)] + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + From 47e9afa20bf5b4d1828e2b8c3e5fa049388e664f Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Mon, 9 May 2022 21:48:57 +0800 Subject: [PATCH 08/19] fix clippy --- clippy_lints/src/collapsible_match.rs | 4 ++-- clippy_lints/src/entry.rs | 4 ++-- clippy_lints/src/only_used_in_recursion.rs | 2 +- clippy_lints/src/utils/author.rs | 10 +++++----- clippy_utils/src/hir_utils.rs | 6 ++++-- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/collapsible_match.rs index cc354b50afa..826eb0ae6b1 100644 --- a/clippy_lints/src/collapsible_match.rs +++ b/clippy_lints/src/collapsible_match.rs @@ -5,7 +5,7 @@ use clippy_utils::{is_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_s use if_chain::if_chain; use rustc_errors::MultiSpan; use rustc_hir::LangItem::OptionNone; -use rustc_hir::{Arm, Expr, Guard, HirId, Pat, PatKind}; +use rustc_hir::{Arm, Expr, Guard, HirId, Let, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; @@ -109,7 +109,7 @@ fn check_arm<'tcx>( (Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(a, b), }; // the binding must not be used in the if guard - if outer_guard.map_or(true, |(Guard::If(e) | Guard::IfLet(_, e))| !is_local_used(cx, *e, binding_id)); + if outer_guard.map_or(true, |(Guard::If(e) | Guard::IfLet(Let { init: e, .. }))| !is_local_used(cx, *e, binding_id)); // ...or anywhere in the inner expression if match inner { IfLetOrMatch::IfLet(_, _, body, els) => { diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index 1ae2e20c1e0..d3d3ed2c235 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -11,7 +11,7 @@ use rustc_errors::Applicability; use rustc_hir::{ hir_id::HirIdSet, intravisit::{walk_expr, Visitor}, - Block, Expr, ExprKind, Guard, HirId, Pat, Stmt, StmtKind, UnOp, + Block, Expr, ExprKind, Guard, HirId, Let, Pat, Stmt, StmtKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -478,7 +478,7 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { let mut is_map_used = self.is_map_used; for arm in arms { self.visit_pat(arm.pat); - if let Some(Guard::If(guard) | Guard::IfLet(_, guard)) = arm.guard { + if let Some(Guard::If(guard) | Guard::IfLet(&Let { init: guard, .. })) = arm.guard { self.visit_non_tail_expr(guard); } is_map_used |= self.visit_cond_arm(arm.body); diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index beb812793f8..d66698f8adc 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -596,7 +596,7 @@ impl<'tcx> SideEffectVisit<'tcx> { let mut vars = std::mem::take(&mut self.ret_vars); let _ = arm.guard.as_ref().map(|guard| { self.visit_expr(match guard { - Guard::If(expr) | Guard::IfLet(_, expr) => expr, + Guard::If(expr) | Guard::IfLet(Let { init: expr, .. }) => expr, }); vars.append(&mut self.ret_vars); }); diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index ff5be825b78..3f4d0fd199d 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -315,11 +315,11 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { out!("if let Some(Guard::If({expr})) = {arm}.guard;"); self.expr(expr); }, - Some(hir::Guard::IfLet(pat, expr)) => { - bind!(self, pat, expr); - out!("if let Some(Guard::IfLet({pat}, {expr}) = {arm}.guard;"); - self.pat(pat); - self.expr(expr); + Some(hir::Guard::IfLet(let_expr)) => { + bind!(self, let_expr); + out!("if let Some(Guard::IfLet({let_expr}) = {arm}.guard;"); + self.pat(field!(let_expr.pat)); + self.expr(field!(let_expr.init)); }, } self.expr(field!(arm.body)); diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index f4da625f1e3..aa21f15ee5d 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -301,7 +301,9 @@ impl HirEqInterExpr<'_, '_, '_> { fn eq_guard(&mut self, left: &Guard<'_>, right: &Guard<'_>) -> bool { match (left, right) { (Guard::If(l), Guard::If(r)) => self.eq_expr(l, r), - (Guard::IfLet(lp, le), Guard::IfLet(rp, re)) => self.eq_pat(lp, rp) && self.eq_expr(le, re), + (Guard::IfLet(l), Guard::IfLet(r)) => { + self.eq_pat(l.pat, r.pat) && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) && self.eq_expr(l.init, r.init) + }, _ => false, } } @@ -894,7 +896,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { pub fn hash_guard(&mut self, g: &Guard<'_>) { match g { - Guard::If(expr) | Guard::IfLet(_, expr) => { + Guard::If(expr) | Guard::IfLet(Let { init: expr, .. }) => { self.hash_expr(expr); }, } From 107ee4067414dc02da091ce49fb491695bbb52f0 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 26 Apr 2022 14:04:23 +0200 Subject: [PATCH 09/19] update clippy --- clippy_lints/src/derivable_impls.rs | 6 +++--- clippy_lints/src/derive.rs | 10 +++++----- clippy_lints/src/functions/must_use.rs | 8 ++++---- clippy_lints/src/manual_non_exhaustive.rs | 3 +-- clippy_lints/src/matches/match_wild_enum.rs | 3 +-- clippy_lints/src/new_without_default.rs | 2 +- clippy_lints/src/partialeq_ne_impl.rs | 4 +--- clippy_lints/src/significant_drop_in_scrutinee.rs | 2 +- clippy_utils/src/attrs.rs | 7 ++----- clippy_utils/src/lib.rs | 15 ++------------- clippy_utils/src/ty.rs | 12 ++++++------ 11 files changed, 27 insertions(+), 45 deletions(-) diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs index 14098340745..34a5f8444de 100644 --- a/clippy_lints/src/derivable_impls.rs +++ b/clippy_lints/src/derivable_impls.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{is_automatically_derived, is_default_equivalent, peel_blocks}; +use clippy_utils::{is_default_equivalent, peel_blocks}; use rustc_hir::{ def::{DefKind, Res}, Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind, @@ -71,8 +71,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { self_ty, .. }) = item.kind; - if let attrs = cx.tcx.hir().attrs(item.hir_id()); - if !is_automatically_derived(attrs); + if !cx.tcx.has_attr(item.def_id.to_def_id(), sym::automatically_derived); if !item.span.from_expansion(); if let Some(def_id) = trait_ref.trait_def_id(); if cx.tcx.is_diagnostic_item(sym::Default, def_id); @@ -81,6 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { if let ImplItemKind::Fn(_, b) = &impl_item.kind; if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b); if let Some(adt_def) = cx.tcx.type_of(item.def_id).ty_adt_def(); + if let attrs = cx.tcx.hir().attrs(item.hir_id()); if !attrs.iter().any(|attr| attr.doc_str().is_some()); if let child_attrs = cx.tcx.hir().attrs(impl_item_hir); if !child_attrs.iter().any(|attr| attr.doc_str().is_some()); diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 557e101494e..545bc7d2103 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_then}; use clippy_utils::paths; use clippy_utils::ty::{implements_trait, is_copy}; -use clippy_utils::{is_automatically_derived, is_lint_allowed, match_def_path}; +use clippy_utils::{is_lint_allowed, match_def_path}; use if_chain::if_chain; use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor}; use rustc_hir::{ @@ -171,8 +171,8 @@ impl<'tcx> LateLintPass<'tcx> for Derive { }) = item.kind { let ty = cx.tcx.type_of(item.def_id); - let attrs = cx.tcx.hir().attrs(item.hir_id()); - let is_automatically_derived = is_automatically_derived(attrs); + let is_automatically_derived = + cx.tcx.has_attr(item.def_id.to_def_id(), sym::automatically_derived); check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived); check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived); @@ -201,7 +201,7 @@ fn check_hash_peq<'tcx>( then { // Look for the PartialEq implementations for `ty` cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| { - let peq_is_automatically_derived = is_automatically_derived(cx.tcx.get_attrs(impl_id)); + let peq_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived); if peq_is_automatically_derived == hash_is_automatically_derived { return; @@ -255,7 +255,7 @@ fn check_ord_partial_ord<'tcx>( then { // Look for the PartialOrd implementations for `ty` cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| { - let partial_ord_is_automatically_derived = is_automatically_derived(cx.tcx.get_attrs(impl_id)); + let partial_ord_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived); if partial_ord_is_automatically_derived == ord_is_automatically_derived { return; diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index 38e943d2eb8..6672a6cb0b5 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -13,13 +13,13 @@ use clippy_utils::attrs::is_proc_macro; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_must_use_ty; -use clippy_utils::{match_def_path, must_use_attr, return_ty, trait_ref_of_method}; +use clippy_utils::{match_def_path, return_ty, trait_ref_of_method}; use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT}; pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); - let attr = must_use_attr(attrs); + let attr = cx.tcx.get_attr(item.def_id.to_def_id(), sym::must_use); if let hir::ItemKind::Fn(ref sig, _generics, ref body_id) = item.kind { let is_public = cx.access_levels.is_exported(item.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); @@ -44,7 +44,7 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp let is_public = cx.access_levels.is_exported(item.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); let attrs = cx.tcx.hir().attrs(item.hir_id()); - let attr = must_use_attr(attrs); + let attr = cx.tcx.get_attr(item.def_id.to_def_id(), sym::must_use); if let Some(attr) = attr { check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr); } else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.def_id).is_none() { @@ -67,7 +67,7 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); let attrs = cx.tcx.hir().attrs(item.hir_id()); - let attr = must_use_attr(attrs); + let attr = cx.tcx.get_attr(item.def_id.to_def_id(), sym::must_use); if let Some(attr) = attr { check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr); } else if let hir::TraitFn::Provided(eid) = *eid { diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index b8d620d8171..09164690700 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -1,4 +1,3 @@ -use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; use clippy_utils::{is_lint_allowed, meets_msrv, msrvs}; @@ -161,7 +160,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { let id = cx.tcx.hir().local_def_id(v.id); (matches!(v.data, hir::VariantData::Unit(_)) && v.ident.as_str().starts_with('_') - && is_doc_hidden(cx.tcx.get_attrs(id.to_def_id()))) + && cx.tcx.is_doc_hidden(id.to_def_id())) .then(|| (id, v.span)) }); if let Some((id, span)) = iter.next() diff --git a/clippy_lints/src/matches/match_wild_enum.rs b/clippy_lints/src/matches/match_wild_enum.rs index 93bf0dc62e0..fc45ccee185 100644 --- a/clippy_lints/src/matches/match_wild_enum.rs +++ b/clippy_lints/src/matches/match_wild_enum.rs @@ -193,6 +193,5 @@ impl<'a> CommonPrefixSearcher<'a> { } fn is_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool { - let attrs = cx.tcx.get_attrs(variant_def.def_id); - clippy_utils::attrs::is_doc_hidden(attrs) || clippy_utils::attrs::is_unstable(attrs) + cx.tcx.is_doc_hidden(variant_def.def_id) || cx.tcx.has_attr(variant_def.def_id, sym::unstable) } diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index 2f733f221d5..2bdccb42507 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -85,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { // can't be implemented for unsafe new return; } - if clippy_utils::is_doc_hidden(cx.tcx.hir().attrs(id)) { + if cx.tcx.is_doc_hidden(impl_item.def_id) { // shouldn't be implemented when it is hidden in docs return; } diff --git a/clippy_lints/src/partialeq_ne_impl.rs b/clippy_lints/src/partialeq_ne_impl.rs index 1469cb434c0..09ac514d014 100644 --- a/clippy_lints/src/partialeq_ne_impl.rs +++ b/clippy_lints/src/partialeq_ne_impl.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_hir; -use clippy_utils::is_automatically_derived; use if_chain::if_chain; use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -37,8 +36,7 @@ impl<'tcx> LateLintPass<'tcx> for PartialEqNeImpl { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if_chain! { if let ItemKind::Impl(Impl { of_trait: Some(ref trait_ref), items: impl_items, .. }) = item.kind; - let attrs = cx.tcx.hir().attrs(item.hir_id()); - if !is_automatically_derived(attrs); + if !cx.tcx.has_attr(item.def_id.to_def_id(), sym::automatically_derived); if let Some(eq_trait) = cx.tcx.lang_items().eq_trait(); if trait_ref.path.res.def_id() == eq_trait; then { diff --git a/clippy_lints/src/significant_drop_in_scrutinee.rs b/clippy_lints/src/significant_drop_in_scrutinee.rs index 94ae0c8f5a6..f300acf0fb2 100644 --- a/clippy_lints/src/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/significant_drop_in_scrutinee.rs @@ -290,7 +290,7 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> { fn has_sig_drop_attr(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { if let Some(adt) = ty.ty_adt_def() { - if get_attr(cx.sess(), cx.tcx.get_attrs(adt.did()), "has_significant_drop").count() > 0 { + if get_attr(cx.sess(), cx.tcx.get_attrs_unchecked(adt.did()), "has_significant_drop").count() > 0 { return true; } } diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index 7f448175e32..904b1a05ccc 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -1,6 +1,7 @@ -use rustc_ast::{ast, attr}; +use rustc_ast::ast; use rustc_errors::Applicability; use rustc_session::Session; +use rustc_ast::attr; use rustc_span::sym; use std::str::FromStr; @@ -158,7 +159,3 @@ pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool { .any(|l| attr::list_contains_name(&l, sym::hidden)) } -/// Return true if the attributes contain `#[unstable]` -pub fn is_unstable(attrs: &[ast::Attribute]) -> bool { - attrs.iter().any(|attr| attr.has_name(sym::unstable)) -} diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 7d46952d971..98a073d122e 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -66,7 +66,7 @@ use std::lazy::SyncOnceCell; use std::sync::{Mutex, MutexGuard}; use if_chain::if_chain; -use rustc_ast::ast::{self, Attribute, LitKind}; +use rustc_ast::ast::{self, LitKind}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::unhash::UnhashMap; use rustc_hir as hir; @@ -1472,12 +1472,6 @@ pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx> } } -/// Checks for the `#[automatically_derived]` attribute all `#[derive]`d -/// implementations have. -pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool { - has_attr(attrs, sym::automatically_derived) -} - pub fn is_self(slf: &Param<'_>) -> bool { if let PatKind::Binding(.., name, _) = slf.pat.kind { name.name == kw::SelfLower @@ -1724,11 +1718,6 @@ pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'t None } -// Finds the `#[must_use]` attribute, if any -pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> { - attrs.iter().find(|a| a.has_name(sym::must_use)) -} - // check if expr is calling method or function with #[must_use] attribute pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let did = match expr.kind { @@ -1745,7 +1734,7 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { _ => None, }; - did.map_or(false, |did| must_use_attr(cx.tcx.get_attrs(did)).is_some()) + did.map_or(false, |did| cx.tcx.has_attr(did, sym::must_use)) } /// Checks if an expression represents the identity function diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 901e3e5390c..7f14a306d0e 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -22,7 +22,7 @@ use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::query::normalize::AtExt; use std::iter; -use crate::{match_def_path, must_use_attr, path_res, paths}; +use crate::{match_def_path, path_res, paths}; // Checks if the given type implements copy. pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { @@ -178,18 +178,18 @@ pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { // Returns whether the type has #[must_use] attribute pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { match ty.kind() { - ty::Adt(adt, _) => must_use_attr(cx.tcx.get_attrs(adt.did())).is_some(), - ty::Foreign(ref did) => must_use_attr(cx.tcx.get_attrs(*did)).is_some(), + ty::Adt(adt, _) => cx.tcx.has_attr(adt.did(), sym::must_use), + ty::Foreign(did) => cx.tcx.has_attr(*did, sym::must_use), ty::Slice(ty) | ty::Array(ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) => { // for the Array case we don't need to care for the len == 0 case // because we don't want to lint functions returning empty arrays is_must_use_ty(cx, *ty) }, ty::Tuple(substs) => substs.iter().any(|ty| is_must_use_ty(cx, ty)), - ty::Opaque(ref def_id, _) => { + ty::Opaque(def_id, _) => { for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) { if let ty::PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder() { - if must_use_attr(cx.tcx.get_attrs(trait_predicate.trait_ref.def_id)).is_some() { + if cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) { return true; } } @@ -199,7 +199,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { ty::Dynamic(binder, _) => { for predicate in binder.iter() { if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() { - if must_use_attr(cx.tcx.get_attrs(trait_ref.def_id)).is_some() { + if cx.tcx.has_attr(trait_ref.def_id, sym::must_use) { return true; } } From cf8ea64d9dd9f8925ce328523e075be5f18efeaf Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Sun, 8 May 2022 01:17:58 -0400 Subject: [PATCH 10/19] Introduce EarlyBinder --- clippy_lints/src/eta_reduction.rs | 4 ++-- clippy_lints/src/future_not_send.rs | 4 ++-- clippy_lints/src/mut_reference.rs | 4 ++-- clippy_lints/src/transmute/transmute_undefined_repr.rs | 4 ++-- clippy_utils/src/consts.rs | 4 ++-- clippy_utils/src/ty.rs | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 1b19868e4c7..b7776b3f0c3 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -12,7 +12,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::binding::BindingMode; use rustc_middle::ty::subst::Subst; -use rustc_middle::ty::{self, ClosureKind, Ty, TypeFoldable}; +use rustc_middle::ty::{self, ClosureKind, EarlyBinder, Ty, TypeFoldable}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::sym; @@ -150,7 +150,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { if check_inputs(cx, body.params, args); let method_def_id = cx.typeck_results().type_dependent_def_id(body.value.hir_id).unwrap(); let substs = cx.typeck_results().node_substs(body.value.hir_id); - let call_ty = cx.tcx.type_of(method_def_id).subst(cx.tcx, substs); + let call_ty = EarlyBinder(cx.tcx.type_of(method_def_id)).subst(cx.tcx, substs); if check_sig(cx, closure_ty, call_ty); then { span_lint_and_then(cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, expr.span, "redundant closure", |diag| { diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index 43911a313d5..5c46d6c7df7 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -5,7 +5,7 @@ use rustc_hir::{Body, FnDecl, HirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::subst::Subst; -use rustc_middle::ty::{Opaque, PredicateKind::Trait}; +use rustc_middle::ty::{EarlyBinder, Opaque, PredicateKind::Trait}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, Span}; use rustc_trait_selection::traits::error_reporting::suggestions::InferCtxtExt; @@ -67,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { let preds = cx.tcx.explicit_item_bounds(id); let mut is_future = false; for &(p, _span) in preds { - let p = p.subst(cx.tcx, subst); + let p = EarlyBinder(p).subst(cx.tcx, subst); if let Some(trait_pred) = p.to_opt_poly_trait_pred() { if Some(trait_pred.skip_binder().trait_ref.def_id) == cx.tcx.lang_items().future_trait() { is_future = true; diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs index 5c3e505c06c..a323737ae40 100644 --- a/clippy_lints/src/mut_reference.rs +++ b/clippy_lints/src/mut_reference.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::subst::Subst; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, EarlyBinder, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use std::iter; @@ -48,7 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed { ExprKind::MethodCall(path, arguments, _) => { let def_id = cx.typeck_results().type_dependent_def_id(e.hir_id).unwrap(); let substs = cx.typeck_results().node_substs(e.hir_id); - let method_type = cx.tcx.type_of(def_id).subst(cx.tcx, substs); + let method_type = EarlyBinder(cx.tcx.type_of(def_id)).subst(cx.tcx, substs); check_arguments(cx, arguments, method_type, path.ident.as_str(), "method"); }, _ => (), diff --git a/clippy_lints/src/transmute/transmute_undefined_repr.rs b/clippy_lints/src/transmute/transmute_undefined_repr.rs index f5e21267a89..0bb577b7620 100644 --- a/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -4,7 +4,7 @@ use clippy_utils::ty::is_c_void; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::subst::{Subst, SubstsRef}; -use rustc_middle::ty::{self, IntTy, Ty, TypeAndMut, UintTy}; +use rustc_middle::ty::{self, EarlyBinder, IntTy, Ty, TypeAndMut, UintTy}; use rustc_span::Span; #[allow(clippy::too_many_lines)] @@ -307,7 +307,7 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> .non_enum_variant() .fields .iter() - .map(|f| cx.tcx.type_of(f.did).subst(cx.tcx, substs)); + .map(|f| EarlyBinder(cx.tcx.type_of(f.did)).subst(cx.tcx, substs)); let Some(sized_ty) = iter.find(|&ty| !is_zero_sized_ty(cx, ty)) else { return ReducedTy::TypeErasure; }; diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index fdb822c3e5b..a80c7ee4929 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -9,7 +9,7 @@ use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, Item, ItemKind, use rustc_lint::LateContext; use rustc_middle::mir::interpret::Scalar; use rustc_middle::ty::subst::{Subst, SubstsRef}; -use rustc_middle::ty::{self, FloatTy, ScalarInt, Ty, TyCtxt}; +use rustc_middle::ty::{self, EarlyBinder, FloatTy, ScalarInt, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_span::symbol::Symbol; use std::cmp::Ordering::{self, Equal}; @@ -420,7 +420,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { let substs = if self.substs.is_empty() { substs } else { - substs.subst(self.lcx.tcx, self.substs) + EarlyBinder(substs).subst(self.lcx.tcx, self.substs) }; let result = self diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 901e3e5390c..b46a7b86a75 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -13,7 +13,7 @@ use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst}; use rustc_middle::ty::{ - self, AdtDef, Binder, FnSig, IntTy, Predicate, PredicateKind, Ty, TyCtxt, TypeFoldable, UintTy, VariantDiscr, + self, AdtDef, Binder, EarlyBinder, FnSig, IntTy, Predicate, PredicateKind, Ty, TyCtxt, TypeFoldable, UintTy, VariantDiscr, }; use rustc_span::symbol::Ident; use rustc_span::{sym, Span, Symbol, DUMMY_SP}; @@ -520,7 +520,7 @@ pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option Some(ExprFnSig::Closure(subs.as_closure().sig())), - ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.fn_sig(id).subst(cx.tcx, subs))), + ty::FnDef(id, subs) => Some(ExprFnSig::Sig(EarlyBinder(cx.tcx.fn_sig(id)).subst(cx.tcx, subs))), ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig)), ty::Dynamic(bounds, _) => { let lang_items = cx.tcx.lang_items(); From 31825326e5bb0a19bfc2f1c2b862787c43c0ee5f Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Wed, 11 May 2022 18:51:14 +0200 Subject: [PATCH 11/19] Bless clippy. --- tests/ui/crashes/ice-6252.stderr | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/ui/crashes/ice-6252.stderr b/tests/ui/crashes/ice-6252.stderr index c8239897f3a..d930d486fde 100644 --- a/tests/ui/crashes/ice-6252.stderr +++ b/tests/ui/crashes/ice-6252.stderr @@ -30,7 +30,15 @@ LL | const VAL: T; LL | impl TypeVal for Multiply where N: TypeVal {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `VAL` in implementation -error: aborting due to 3 previous errors +error: constant expression depends on a generic parameter + --> $DIR/ice-6252.rs:13:9 + | +LL | [1; >::VAL]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to 4 previous errors Some errors have detailed explanations: E0046, E0412. For more information about an error, try `rustc --explain E0046`. From b622f56d398155d3456887ed0025b391fed8b42c Mon Sep 17 00:00:00 2001 From: Miguel Guarniz Date: Sat, 7 May 2022 14:51:05 -0400 Subject: [PATCH 12/19] remove TestItemNamesVisitor Signed-off-by: Miguel Guarniz --- clippy_utils/src/lib.rs | 58 ++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 35 deletions(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 98a073d122e..6db7f247a99 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -74,11 +74,10 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_ID}; use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; -use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk}; use rustc_hir::{ def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr, ExprKind, FnDecl, - ForeignItem, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, + HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp, }; @@ -2068,35 +2067,6 @@ pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { false } -struct TestItemNamesVisitor<'tcx> { - tcx: TyCtxt<'tcx>, - names: Vec, -} - -impl<'hir> ItemLikeVisitor<'hir> for TestItemNamesVisitor<'hir> { - fn visit_item(&mut self, item: &Item<'_>) { - if let ItemKind::Const(ty, _body) = item.kind { - if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind { - // We could also check for the type name `test::TestDescAndFn` - if let Res::Def(DefKind::Struct, _) = path.res { - let has_test_marker = self - .tcx - .hir() - .attrs(item.hir_id()) - .iter() - .any(|a| a.has_name(sym::rustc_test_marker)); - if has_test_marker { - self.names.push(item.ident.name); - } - } - } - } - } - fn visit_trait_item(&mut self, _: &TraitItem<'_>) {} - fn visit_impl_item(&mut self, _: &ImplItem<'_>) {} - fn visit_foreign_item(&mut self, _: &ForeignItem<'_>) {} -} - static TEST_ITEM_NAMES_CACHE: SyncOnceCell>>> = SyncOnceCell::new(); fn with_test_item_names<'tcx>(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool { @@ -2105,10 +2075,28 @@ fn with_test_item_names<'tcx>(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn( match map.entry(module) { Entry::Occupied(entry) => f(entry.get()), Entry::Vacant(entry) => { - let mut visitor = TestItemNamesVisitor { tcx, names: Vec::new() }; - tcx.hir().visit_item_likes_in_module(module, &mut visitor); - visitor.names.sort_unstable(); - f(&*entry.insert(visitor.names)) + let mut names = Vec::new(); + for id in tcx.hir().module_items(module) { + if matches!(tcx.def_kind(id.def_id), DefKind::Const) + && let item = tcx.hir().item(id) + && let ItemKind::Const(ty, _body) = item.kind { + if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind { + // We could also check for the type name `test::TestDescAndFn` + if let Res::Def(DefKind::Struct, _) = path.res { + let has_test_marker = tcx + .hir() + .attrs(item.hir_id()) + .iter() + .any(|a| a.has_name(sym::rustc_test_marker)); + if has_test_marker { + names.push(item.ident.name); + } + } + } + } + } + names.sort_unstable(); + f(&*entry.insert(names)) }, } } From cc0607a5b184901c9f7dacebd60838235c745390 Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Sun, 8 May 2022 15:12:56 -0400 Subject: [PATCH 13/19] Add bound_type_of --- clippy_lints/src/eta_reduction.rs | 4 ++-- clippy_lints/src/mut_reference.rs | 4 ++-- clippy_lints/src/transmute/transmute_undefined_repr.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index b7776b3f0c3..530d6d4de35 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -12,7 +12,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::binding::BindingMode; use rustc_middle::ty::subst::Subst; -use rustc_middle::ty::{self, ClosureKind, EarlyBinder, Ty, TypeFoldable}; +use rustc_middle::ty::{self, ClosureKind, Ty, TypeFoldable}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::sym; @@ -150,7 +150,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { if check_inputs(cx, body.params, args); let method_def_id = cx.typeck_results().type_dependent_def_id(body.value.hir_id).unwrap(); let substs = cx.typeck_results().node_substs(body.value.hir_id); - let call_ty = EarlyBinder(cx.tcx.type_of(method_def_id)).subst(cx.tcx, substs); + let call_ty = cx.tcx.bound_type_of(method_def_id).subst(cx.tcx, substs); if check_sig(cx, closure_ty, call_ty); then { span_lint_and_then(cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, expr.span, "redundant closure", |diag| { diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs index a323737ae40..9d8f8999ce4 100644 --- a/clippy_lints/src/mut_reference.rs +++ b/clippy_lints/src/mut_reference.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::subst::Subst; -use rustc_middle::ty::{self, EarlyBinder, Ty}; +use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use std::iter; @@ -48,7 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed { ExprKind::MethodCall(path, arguments, _) => { let def_id = cx.typeck_results().type_dependent_def_id(e.hir_id).unwrap(); let substs = cx.typeck_results().node_substs(e.hir_id); - let method_type = EarlyBinder(cx.tcx.type_of(def_id)).subst(cx.tcx, substs); + let method_type = cx.tcx.bound_type_of(def_id).subst(cx.tcx, substs); check_arguments(cx, arguments, method_type, path.ident.as_str(), "method"); }, _ => (), diff --git a/clippy_lints/src/transmute/transmute_undefined_repr.rs b/clippy_lints/src/transmute/transmute_undefined_repr.rs index 0bb577b7620..be6277332db 100644 --- a/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -4,7 +4,7 @@ use clippy_utils::ty::is_c_void; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::subst::{Subst, SubstsRef}; -use rustc_middle::ty::{self, EarlyBinder, IntTy, Ty, TypeAndMut, UintTy}; +use rustc_middle::ty::{self, IntTy, Ty, TypeAndMut, UintTy}; use rustc_span::Span; #[allow(clippy::too_many_lines)] @@ -307,7 +307,7 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> .non_enum_variant() .fields .iter() - .map(|f| EarlyBinder(cx.tcx.type_of(f.did)).subst(cx.tcx, substs)); + .map(|f| cx.tcx.bound_type_of(f.did).subst(cx.tcx, substs)); let Some(sized_ty) = iter.find(|&ty| !is_zero_sized_ty(cx, ty)) else { return ReducedTy::TypeErasure; }; From 6dab55cfae21ecc91f42b899a80fb91b72ab8e32 Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Sun, 8 May 2022 15:43:18 -0400 Subject: [PATCH 14/19] Add bound_fn_sig --- clippy_utils/src/ty.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index b46a7b86a75..8d99f3002b8 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -13,7 +13,7 @@ use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst}; use rustc_middle::ty::{ - self, AdtDef, Binder, EarlyBinder, FnSig, IntTy, Predicate, PredicateKind, Ty, TyCtxt, TypeFoldable, UintTy, VariantDiscr, + self, AdtDef, Binder, FnSig, IntTy, Predicate, PredicateKind, Ty, TyCtxt, TypeFoldable, UintTy, VariantDiscr, }; use rustc_span::symbol::Ident; use rustc_span::{sym, Span, Symbol, DUMMY_SP}; @@ -520,7 +520,7 @@ pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option Some(ExprFnSig::Closure(subs.as_closure().sig())), - ty::FnDef(id, subs) => Some(ExprFnSig::Sig(EarlyBinder(cx.tcx.fn_sig(id)).subst(cx.tcx, subs))), + ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs))), ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig)), ty::Dynamic(bounds, _) => { let lang_items = cx.tcx.lang_items(); From 0f308662596f2a85c4aee00a3361d0de8c9b34d7 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 13 May 2022 13:50:21 +0000 Subject: [PATCH 15/19] Add a query for checking whether a function is an intrinsic. --- clippy_utils/src/qualify_min_const_fn.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 75808b1b174..66d373a1bf8 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -14,7 +14,6 @@ use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt}; use rustc_semver::RustcVersion; use rustc_span::symbol::sym; use rustc_span::Span; -use rustc_target::spec::abi::Abi::RustIntrinsic; use std::borrow::Cow; type McfResult = Result<(), (Span, Cow<'static, str>)>; @@ -323,7 +322,7 @@ fn check_terminator<'a, 'tcx>( // within const fns. `transmute` is allowed in all other const contexts. // This won't really scale to more intrinsics or functions. Let's allow const // transmutes in const fn before we add more hacks to this. - if tcx.fn_sig(fn_def_id).abi() == RustIntrinsic && tcx.item_name(fn_def_id) == sym::transmute { + if tcx.is_intrinsic(fn_def_id) && tcx.item_name(fn_def_id) == sym::transmute { return Err(( span, "can only call `transmute` from const items, not `const fn`".into(), From d7a6654580d5765777afd31b00110f87893825d0 Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Tue, 17 May 2022 08:41:01 +0800 Subject: [PATCH 16/19] Change `Successors` to `impl Iterator` --- clippy_lints/src/redundant_clone.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 1507c75ff61..37aac8b2a49 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -114,7 +114,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { } // Give up on loops - if terminator.successors().any(|s| *s == bb) { + if terminator.successors().any(|s| s == bb) { continue; } @@ -440,7 +440,7 @@ fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>, // Short-circuit if (usage.cloned_used && usage.clone_consumed_or_mutated) || // Give up on loops - tdata.terminator().successors().any(|s| *s == bb) + tdata.terminator().successors().any(|s| s == bb) { return CloneUsage { cloned_used: true, From 5b72632c98a6e45dfe2c1aaddad1b05f5259a914 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Fri, 20 May 2022 21:03:24 +0200 Subject: [PATCH 17/19] move to sus and fix dogfood --- clippy_lints/src/lib.register_all.rs | 1 + clippy_lints/src/lib.register_nursery.rs | 1 - clippy_lints/src/lib.register_suspicious.rs | 1 + .../src/significant_drop_in_scrutinee.rs | 26 +++++++++---------- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 48ceeb43ee7..be5c478900f 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -278,6 +278,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(self_assignment::SELF_ASSIGNMENT), LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS), LintId::of(serde_api::SERDE_API_MISUSE), + LintId::of(significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE), LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index d43c7e03533..ec187563b3f 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -25,7 +25,6 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE), LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE), LintId::of(regex::TRIVIAL_REGEX), - LintId::of(significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE), LintId::of(strings::STRING_LIT_AS_BYTES), LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY), diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs index 338a6a28bc9..2de49f1624a 100644 --- a/clippy_lints/src/lib.register_suspicious.rs +++ b/clippy_lints/src/lib.register_suspicious.rs @@ -27,6 +27,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(mut_key::MUTABLE_KEY_TYPE), LintId::of(octal_escapes::OCTAL_ESCAPES), LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT), + LintId::of(significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE), LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), ]) diff --git a/clippy_lints/src/significant_drop_in_scrutinee.rs b/clippy_lints/src/significant_drop_in_scrutinee.rs index f300acf0fb2..4cebfd7a93f 100644 --- a/clippy_lints/src/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/significant_drop_in_scrutinee.rs @@ -83,7 +83,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.60.0"] pub SIGNIFICANT_DROP_IN_SCRUTINEE, - nursery, + suspicious, "warns when a temporary of a type with a drop with a significant side-effect might have a surprising lifetime" } @@ -99,7 +99,7 @@ impl<'tcx> LateLintPass<'tcx> for SignificantDropInScrutinee { found.found_span, "temporary with significant drop in match scrutinee", |diag| set_diagnostic(diag, cx, expr, found), - ) + ); } } } @@ -148,8 +148,8 @@ fn set_diagnostic<'tcx>(diag: &mut Diagnostic, cx: &LateContext<'tcx>, expr: &'t ); } -/// If the expression is an ExprKind::Match, check if the scrutinee has a significant drop that may -/// have a surprising lifetime. +/// If the expression is an `ExprKind::Match`, check if the scrutinee has a significant drop that +/// may have a surprising lifetime. fn has_significant_drop_in_scrutinee<'tcx, 'a>( cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>, @@ -171,6 +171,7 @@ struct SigDropHelper<'a, 'tcx> { special_handling_for_binary_op: bool, } +#[expect(clippy::enum_variant_names)] #[derive(Debug, PartialEq, Eq, Clone, Copy)] enum LintSuggestion { MoveOnly, @@ -213,7 +214,7 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> { } /// This will try to set the current suggestion (so it can be moved into the suggestions vec - /// later). If allow_move_and_clone is false, the suggestion *won't* be set -- this gives us + /// later). If `allow_move_and_clone` is false, the suggestion *won't* be set -- this gives us /// an opportunity to look for another type in the chain that will be trivially copyable. /// However, if we are at the the end of the chain, we want to accept whatever is there. (The /// suggestion won't actually be output, but the diagnostic message will be output, so the user @@ -313,10 +314,10 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> { } false }, - rustc_middle::ty::Array(ty, _) => self.has_sig_drop_attr(cx, *ty), - rustc_middle::ty::RawPtr(TypeAndMut { ty, .. }) => self.has_sig_drop_attr(cx, *ty), - rustc_middle::ty::Ref(_, ty, _) => self.has_sig_drop_attr(cx, *ty), - rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(cx, *ty), + rustc_middle::ty::Array(ty, _) + | rustc_middle::ty::RawPtr(TypeAndMut { ty, .. }) + | rustc_middle::ty::Ref(_, ty, _) + | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(cx, *ty), _ => false, } } @@ -332,15 +333,12 @@ impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> { match ex.kind { ExprKind::MethodCall(_, [ref expr, ..], _) => { - self.visit_expr(expr) + self.visit_expr(expr); } ExprKind::Binary(_, left, right) => { self.visit_exprs_for_binary_ops(left, right, false, ex.span); } - ExprKind::Assign(left, right, _) => { - self.visit_exprs_for_binary_ops(left, right, true, ex.span); - } - ExprKind::AssignOp(_, left, right) => { + ExprKind::Assign(left, right, _) | ExprKind::AssignOp(_, left, right) => { self.visit_exprs_for_binary_ops(left, right, true, ex.span); } ExprKind::Tup(exprs) => { From 8283238973f67753ffdfb358fd01a10951762716 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Fri, 20 May 2022 21:12:41 +0200 Subject: [PATCH 18/19] Update clippy version `0.1.62` -> `0.1.63` --- 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 03e6bf03afe..373e720b0d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.62" +version = "0.1.63" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index aebf9a87cab..0a3f04da357 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.62" +version = "0.1.63" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index 0b1fd95c345..c4e0b8448ab 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.62" +version = "0.1.63" edition = "2021" publish = false From 7842dbc4f5af4818c89d830607383bfe349cd7e7 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Fri, 20 May 2022 23:32:09 +0200 Subject: [PATCH 19/19] Remove duplicated code and ignore deadlock test --- clippy_dev/src/main.rs | 21 ------------------- .../src/significant_drop_in_scrutinee.rs | 2 +- 2 files changed, 1 insertion(+), 22 deletions(-) diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 8dd2b7f9534..d5cd7ca96c0 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -319,26 +319,5 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { .help("This lint will be uplifted into rustc"), ), ) - .subcommand( - SubCommand::with_name("rename_lint") - .about("Renames the given lint") - .arg( - Arg::with_name("old_name") - .index(1) - .required(true) - .help("The name of the lint to rename"), - ) - .arg( - Arg::with_name("new_name") - .index(2) - .required_unless("uplift") - .help("The new name of the lint"), - ) - .arg( - Arg::with_name("uplift") - .long("uplift") - .help("This lint will be uplifted into rustc"), - ), - ) .get_matches() } diff --git a/clippy_lints/src/significant_drop_in_scrutinee.rs b/clippy_lints/src/significant_drop_in_scrutinee.rs index 4cebfd7a93f..424b361a905 100644 --- a/clippy_lints/src/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/significant_drop_in_scrutinee.rs @@ -29,7 +29,7 @@ declare_clippy_lint! { /// the match block and thus will not unlock. /// /// ### Example - /// ```rust + /// ```rust.ignore /// # use std::sync::Mutex; /// /// # struct State {}