From ede977cf3131caeaac71e36c32390ca3c5b4deda Mon Sep 17 00:00:00 2001 From: F3real Date: Mon, 9 Aug 2021 09:49:55 +0200 Subject: [PATCH 01/94] Update suggestion in case of index for unnecessary_operation lint --- clippy_lints/src/no_effect.rs | 69 +++++++++++++++++------ tests/ui/unnecessary_operation.fixed | 4 +- tests/ui/unnecessary_operation.stderr | 80 +++++++++++++-------------- 3 files changed, 94 insertions(+), 59 deletions(-) diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index e07518b258687..c50980e1cf6d2 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -94,28 +94,63 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect { if has_no_effect(cx, expr) { span_lint_hir(cx, NO_EFFECT, expr.hir_id, stmt.span, "statement with no effect"); } else if let Some(reduced) = reduce_expression(cx, expr) { - let mut snippet = String::new(); - for e in reduced { + for e in &reduced { if e.span.from_expansion() { return; } - if let Some(snip) = snippet_opt(cx, e.span) { - snippet.push_str(&snip); - snippet.push(';'); - } else { - return; + } + if let ExprKind::Index(..) = &expr.kind { + let snippet; + if_chain! { + if let Some(arr) = snippet_opt(cx, reduced[0].span); + if let Some(func) = snippet_opt(cx, reduced[1].span); + then { + snippet = format!("assert!({}.len() > {});", &arr, &func); + } else { + return; + } } + span_lint_hir_and_then( + cx, + UNNECESSARY_OPERATION, + expr.hir_id, + stmt.span, + "unnecessary operation", + |diag| { + diag.span_suggestion( + stmt.span, + "statement can be written as", + snippet, + Applicability::MaybeIncorrect, + ); + }, + ); + } else { + let mut snippet = String::new(); + for e in reduced { + if let Some(snip) = snippet_opt(cx, e.span) { + snippet.push_str(&snip); + snippet.push(';'); + } else { + return; + } + } + span_lint_hir_and_then( + cx, + UNNECESSARY_OPERATION, + expr.hir_id, + stmt.span, + "unnecessary operation", + |diag| { + diag.span_suggestion( + stmt.span, + "statement can be reduced to", + snippet, + Applicability::MachineApplicable, + ); + }, + ); } - span_lint_hir_and_then( - cx, - UNNECESSARY_OPERATION, - expr.hir_id, - stmt.span, - "statement can be reduced", - |diag| { - diag.span_suggestion(stmt.span, "replace it with", snippet, Applicability::MachineApplicable); - }, - ); } } } diff --git a/tests/ui/unnecessary_operation.fixed b/tests/ui/unnecessary_operation.fixed index 2fca96c4cd556..bf0ec8deb3458 100644 --- a/tests/ui/unnecessary_operation.fixed +++ b/tests/ui/unnecessary_operation.fixed @@ -62,10 +62,10 @@ fn main() { get_number(); 5;get_number(); 42;get_number(); - [42, 55];get_usize(); + assert!([42, 55].len() > get_usize()); 42;get_number(); get_number(); - [42; 55];get_usize(); + assert!([42; 55].len() > get_usize()); get_number(); String::from("blah"); diff --git a/tests/ui/unnecessary_operation.stderr b/tests/ui/unnecessary_operation.stderr index f88c9f9908bea..f66d08ecb8281 100644 --- a/tests/ui/unnecessary_operation.stderr +++ b/tests/ui/unnecessary_operation.stderr @@ -1,128 +1,128 @@ -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:51:5 | LL | Tuple(get_number()); - | ^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + | ^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` | = note: `-D clippy::unnecessary-operation` implied by `-D warnings` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:52:5 | LL | Struct { field: get_number() }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:53:5 | LL | Struct { ..get_struct() }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_struct();` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_struct();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:54:5 | LL | Enum::Tuple(get_number()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:55:5 | LL | Enum::Struct { field: get_number() }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:56:5 | LL | 5 + get_number(); - | ^^^^^^^^^^^^^^^^^ help: replace it with: `5;get_number();` + | ^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:57:5 | LL | *&get_number(); - | ^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:58:5 | LL | &get_number(); - | ^^^^^^^^^^^^^^ help: replace it with: `get_number();` + | ^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:59:5 | LL | (5, 6, get_number()); - | ^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `5;6;get_number();` + | ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;6;get_number();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:60:5 | LL | box get_number(); - | ^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + | ^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:61:5 | LL | get_number()..; - | ^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:62:5 | LL | ..get_number(); - | ^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:63:5 | LL | 5..get_number(); - | ^^^^^^^^^^^^^^^^ help: replace it with: `5;get_number();` + | ^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:64:5 | LL | [42, get_number()]; - | ^^^^^^^^^^^^^^^^^^^ help: replace it with: `42;get_number();` + | ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:65:5 | LL | [42, 55][get_usize()]; - | ^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `[42, 55];get_usize();` + | ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42, 55].len() > get_usize());` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:66:5 | LL | (42, get_number()).1; - | ^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `42;get_number();` + | ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:67:5 | LL | [get_number(); 55]; - | ^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + | ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:68:5 | LL | [42; 55][get_usize()]; - | ^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `[42; 55];get_usize();` + | ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42; 55].len() > get_usize());` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:69:5 | LL | / { LL | | get_number() LL | | }; - | |______^ help: replace it with: `get_number();` + | |______^ help: statement can be reduced to: `get_number();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:72:5 | LL | / FooString { LL | | s: String::from("blah"), LL | | }; - | |______^ help: replace it with: `String::from("blah");` + | |______^ help: statement can be reduced to: `String::from("blah");` error: aborting due to 20 previous errors From 8e563b5468d325262d38ecba873bfd3fd3708fa4 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Thu, 12 Aug 2021 10:48:16 +0200 Subject: [PATCH 02/94] Bless clippy tests. --- tests/ui/crashes/ice-3969.stderr | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ui/crashes/ice-3969.stderr b/tests/ui/crashes/ice-3969.stderr index 8b2c318acf84e..9a89047f07277 100644 --- a/tests/ui/crashes/ice-3969.stderr +++ b/tests/ui/crashes/ice-3969.stderr @@ -6,7 +6,7 @@ LL | for<'a> Dst: Sized, | = note: `-D bare-trait-objects` implied by `-D warnings` = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! - = note: for more information, see issue #80165 + = note: for more information, see error: trait objects without an explicit `dyn` are deprecated --> $DIR/ice-3969.rs:27:16 @@ -15,7 +15,7 @@ LL | let x: Dst = *(Box::new(Dst { x: 1 }) as Box>); | ^ help: use `dyn`: `dyn A` | = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! - = note: for more information, see issue #80165 + = note: for more information, see error: trait objects without an explicit `dyn` are deprecated --> $DIR/ice-3969.rs:27:57 @@ -24,7 +24,7 @@ LL | let x: Dst = *(Box::new(Dst { x: 1 }) as Box>); | ^ help: use `dyn`: `dyn A` | = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! - = note: for more information, see issue #80165 + = note: for more information, see error: aborting due to 3 previous errors From 1ad54642008b185f5691e5cac1148147d29fc00f Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 12 Aug 2021 11:16:25 +0200 Subject: [PATCH 03/94] Merge commit '7bfc26ec8e7a454786668e7e52ffe527fc649735' into clippyup --- .github/ISSUE_TEMPLATE/new_lint.md | 5 +- .github/workflows/clippy.yml | 6 +- .github/workflows/clippy_bors.yml | 6 +- .github/workflows/remark.yml | 2 + CHANGELOG.md | 141 +++++++++++- clippy_lints/src/as_conversions.rs | 4 +- clippy_lints/src/doc.rs | 20 +- clippy_lints/src/else_if_without_else.rs | 4 +- clippy_lints/src/functions/mod.rs | 2 +- clippy_lints/src/functions/too_many_lines.rs | 13 +- clippy_lints/src/if_not_else.rs | 4 +- clippy_lints/src/items_after_statements.rs | 6 +- clippy_lints/src/len_zero.rs | 23 +- clippy_lints/src/lib.rs | 3 + clippy_lints/src/literal_representation.rs | 6 +- clippy_lints/src/loops/never_loop.rs | 42 +++- .../src/loops/while_let_on_iterator.rs | 15 +- clippy_lints/src/methods/extend_with_drain.rs | 8 +- .../methods/from_iter_instead_of_collect.rs | 5 +- clippy_lints/src/methods/map_flatten.rs | 42 ++-- clippy_lints/src/methods/mod.rs | 32 ++- clippy_lints/src/methods/or_fun_call.rs | 20 +- .../src/methods/unwrap_or_else_default.rs | 45 ++++ clippy_lints/src/misc_early/mod.rs | 4 +- .../src/misc_early/unneeded_field_pattern.rs | 5 +- clippy_lints/src/needless_borrow.rs | 4 + clippy_lints/src/needless_continue.rs | 2 +- clippy_lints/src/needless_for_each.rs | 2 +- clippy_lints/src/no_effect.rs | 16 +- clippy_lints/src/non_expressive_names.rs | 1 + clippy_lints/src/nonstandard_macro_braces.rs | 13 +- clippy_lints/src/pass_by_ref_or_value.rs | 4 +- clippy_lints/src/redundant_closure_call.rs | 4 +- clippy_lints/src/reference.rs | 3 +- clippy_lints/src/swap.rs | 216 ++++++++---------- clippy_lints/src/types/mod.rs | 2 +- clippy_lints/src/unnested_or_patterns.rs | 2 - clippy_lints/src/unused_unit.rs | 25 +- clippy_lints/src/utils/conf.rs | 134 ++++++++--- .../internal_lints/metadata_collector.rs | 9 +- clippy_utils/src/diagnostics.rs | 6 +- clippy_utils/src/higher.rs | 6 +- clippy_utils/src/lib.rs | 67 ++++++ clippy_utils/src/source.rs | 2 +- clippy_utils/src/ty.rs | 2 +- doc/basics.md | 6 +- rust-toolchain | 2 +- tests/ui-toml/functions_maxlines/test.rs | 16 ++ tests/ui-toml/functions_maxlines/test.stderr | 26 ++- .../conf_nonstandard_macro_braces.rs | 10 +- .../conf_nonstandard_macro_braces.stderr | 28 +-- tests/{ => ui}/auxiliary/test_macro.rs | 0 tests/ui/extend_with_drain.fixed | 7 +- tests/ui/extend_with_drain.rs | 7 +- tests/ui/extend_with_drain.stderr | 8 +- tests/ui/implicit_hasher.rs | 2 +- tests/ui/map_flatten.fixed | 4 + tests/ui/map_flatten.rs | 4 + tests/ui/map_flatten.stderr | 20 +- tests/ui/never_loop.stderr | 5 + tests/ui/no_effect.stderr | 8 +- tests/ui/or_fun_call.fixed | 19 ++ tests/ui/or_fun_call.rs | 19 ++ tests/ui/or_fun_call.stderr | 58 +++-- tests/ui/similar_names.rs | 3 + tests/ui/similar_names.stderr | 4 +- tests/ui/swap.fixed | 57 ++++- tests/ui/swap.rs | 61 ++++- tests/ui/swap.stderr | 63 ++++- tests/ui/trivially_copy_pass_by_ref.rs | 2 + tests/ui/trivially_copy_pass_by_ref.stderr | 20 +- tests/ui/unwrap_or_else_default.fixed | 71 ++++++ tests/ui/unwrap_or_else_default.rs | 71 ++++++ tests/ui/unwrap_or_else_default.stderr | 22 ++ tests/ui/while_let_on_iterator.fixed | 32 +++ tests/ui/while_let_on_iterator.rs | 32 +++ tests/ui/while_let_on_iterator.stderr | 16 +- 77 files changed, 1311 insertions(+), 375 deletions(-) create mode 100644 clippy_lints/src/methods/unwrap_or_else_default.rs rename tests/{ => ui}/auxiliary/test_macro.rs (100%) create mode 100644 tests/ui/unwrap_or_else_default.fixed create mode 100644 tests/ui/unwrap_or_else_default.rs create mode 100644 tests/ui/unwrap_or_else_default.stderr diff --git a/.github/ISSUE_TEMPLATE/new_lint.md b/.github/ISSUE_TEMPLATE/new_lint.md index e182c99ce06a2..2216bb9f293d7 100644 --- a/.github/ISSUE_TEMPLATE/new_lint.md +++ b/.github/ISSUE_TEMPLATE/new_lint.md @@ -15,8 +15,9 @@ labels: A-lint *What is the advantage of the recommended code over the original code* For example: -- Remove bounce checking inserted by ... -- Remove the need to duplicating/storing/typo ... +- Remove bounds check inserted by ... +- Remove the need to duplicate/store ... +- Remove typo ... ### Drawbacks diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index d856c55a41a4b..0339de77f3cec 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -49,13 +49,13 @@ jobs: echo "LD_LIBRARY_PATH=${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV - name: Build - run: cargo build --features deny-warnings,internal-lints + run: cargo build --features deny-warnings,internal-lints,metadata-collector-lint - name: Test - run: cargo test --features deny-warnings,internal-lints + run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint - name: Test clippy_lints - run: cargo test --features deny-warnings,internal-lints + run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint working-directory: clippy_lints - name: Test rustc_tools_util diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 146b6fccd0c76..1f4d666c7a92c 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -112,13 +112,13 @@ jobs: echo "$SYSROOT/bin" >> $GITHUB_PATH - name: Build - run: cargo build --features deny-warnings,internal-lints + run: cargo build --features deny-warnings,internal-lints,metadata-collector-lint - name: Test - run: cargo test --features deny-warnings,internal-lints + run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint - name: Test clippy_lints - run: cargo test --features deny-warnings,internal-lints + run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint working-directory: clippy_lints - name: Test rustc_tools_util diff --git a/.github/workflows/remark.yml b/.github/workflows/remark.yml index 77efdec1e50db..56c00544c93a7 100644 --- a/.github/workflows/remark.yml +++ b/.github/workflows/remark.yml @@ -20,6 +20,8 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v1.4.4 + with: + node-version: '12.x' - name: Install remark run: npm install remark-cli remark-lint remark-lint-maximum-line-length remark-preset-lint-recommended remark-gfm diff --git a/CHANGELOG.md b/CHANGELOG.md index acbefc8064dda..2b89170073be5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,141 @@ document. ## Unreleased / In Rust Nightly -[3ae8faf...master](https://github.com/rust-lang/rust-clippy/compare/3ae8faf...master) +[74d1561...master](https://github.com/rust-lang/rust-clippy/compare/74d1561...master) + +## Rust 1.55 + +Current beta, release 2021-09-09 + +[3ae8faf...74d1561](https://github.com/rust-lang/rust-clippy/compare/3ae8faf...74d1561) + +### Important Changes + +* Stabilized `cargo clippy --fix` :tada: + [#7405](https://github.com/rust-lang/rust-clippy/pull/7405) + +### New Lints + +* [`rc_mutex`] + [#7316](https://github.com/rust-lang/rust-clippy/pull/7316) +* [`nonstandard_macro_braces`] + [#7299](https://github.com/rust-lang/rust-clippy/pull/7299) +* [`strlen_on_c_strings`] + [#7243](https://github.com/rust-lang/rust-clippy/pull/7243) +* [`self_named_constructors`] + [#7403](https://github.com/rust-lang/rust-clippy/pull/7403) +* [`disallowed_script_idents`] + [#7400](https://github.com/rust-lang/rust-clippy/pull/7400) +* [`disallowed_type`] + [#7315](https://github.com/rust-lang/rust-clippy/pull/7315) +* [`missing_enforced_import_renames`] + [#7300](https://github.com/rust-lang/rust-clippy/pull/7300) +* [`extend_with_drain`] + [#7270](https://github.com/rust-lang/rust-clippy/pull/7270) + +### Moves and Deprecations + +* Moved [`from_iter_instead_of_collect`] to `pedantic` + [#7375](https://github.com/rust-lang/rust-clippy/pull/7375) +* Added `suspicious` as a new lint group for *code that is most likely wrong or useless* + [#7350](https://github.com/rust-lang/rust-clippy/pull/7350) + * Moved [`blanket_clippy_restriction_lints`] to `suspicious` + * Moved [`empty_loop`] to `suspicious` + * Moved [`eval_order_dependence`] to `suspicious` + * Moved [`float_equality_without_abs`] to `suspicious` + * Moved [`for_loops_over_fallibles`] to `suspicious` + * Moved [`misrefactored_assign_op`] to `suspicious` + * Moved [`mut_range_bound`] to `suspicious` + * Moved [`mutable_key_type`] to `suspicious` + * Moved [`suspicious_arithmetic_impl`] to `suspicious` + * Moved [`suspicious_assignment_formatting`] to `suspicious` + * Moved [`suspicious_else_formatting`] to `suspicious` + * Moved [`suspicious_map`] to `suspicious` + * Moved [`suspicious_op_assign_impl`] to `suspicious` + * Moved [`suspicious_unary_op_formatting`] to `suspicious` + +### Enhancements + +* [`while_let_on_iterator`]: Now suggests `&mut iter` inside closures + [#7262](https://github.com/rust-lang/rust-clippy/pull/7262) +* [`doc_markdown`]: + * Now detects unbalanced ticks + [#7357](https://github.com/rust-lang/rust-clippy/pull/7357) + * Add `FreeBSD` to the default configuration as an allowed identifier + [#7334](https://github.com/rust-lang/rust-clippy/pull/7334) +* [`wildcard_enum_match_arm`], [`match_wildcard_for_single_variants`]: Now allows wildcards for enums with unstable + or hidden variants + [#7407](https://github.com/rust-lang/rust-clippy/pull/7407) +* [`redundant_allocation`]: Now additionally supports the `Arc<>` type + [#7308](https://github.com/rust-lang/rust-clippy/pull/7308) +* [`blacklisted_name`]: Now allows blacklisted names in test code + [#7379](https://github.com/rust-lang/rust-clippy/pull/7379) +* [`redundant_closure`]: Suggests `&mut` for `FnMut` + [#7437](https://github.com/rust-lang/rust-clippy/pull/7437) +* [`disallowed_method`], [`disallowed_type`]: The configuration values `disallowed-method` and `disallowed-type` + no longer require fully qualified paths + [#7345](https://github.com/rust-lang/rust-clippy/pull/7345) +* [`zst_offset`]: Fixed lint invocation after it was accidentally suppressed + [#7396](https://github.com/rust-lang/rust-clippy/pull/7396) + +### False Positive Fixes + +* [`default_numeric_fallback`]: No longer lints on float literals as function arguments + [#7446](https://github.com/rust-lang/rust-clippy/pull/7446) +* [`use_self`]: No longer lints on type parameters + [#7288](https://github.com/rust-lang/rust-clippy/pull/7288) +* [`unimplemented`]: Now ignores the `assert` and `debug_assert` macros + [#7439](https://github.com/rust-lang/rust-clippy/pull/7439) +* [`branches_sharing_code`]: Now always checks for block expressions + [#7462](https://github.com/rust-lang/rust-clippy/pull/7462) +* [`field_reassign_with_default`]: No longer triggers in macros + [#7160](https://github.com/rust-lang/rust-clippy/pull/7160) +* [`redundant_clone`]: No longer lints on required clones for borrowed data + [#7346](https://github.com/rust-lang/rust-clippy/pull/7346) +* [`default_numeric_fallback`]: No longer triggers in external macros + [#7325](https://github.com/rust-lang/rust-clippy/pull/7325) +* [`needless_bool`]: No longer lints in macros + [#7442](https://github.com/rust-lang/rust-clippy/pull/7442) +* [`useless_format`]: No longer triggers when additional text is being appended + [#7442](https://github.com/rust-lang/rust-clippy/pull/7442) +* [`assertions_on_constants`]: `cfg!(...)` is no longer considered to be a constant + [#7319](https://github.com/rust-lang/rust-clippy/pull/7319) + +### Suggestion Fixes/Improvements + +* [`needless_collect`]: Now show correct lint messages for shadowed values + [#7289](https://github.com/rust-lang/rust-clippy/pull/7289) +* [`wrong_pub_self_convention`]: The deprecated message now suggest the correct configuration value + [#7382](https://github.com/rust-lang/rust-clippy/pull/7382) +* [`semicolon_if_nothing_returned`]: Allow missing semicolon in blocks with only one expression + [#7326](https://github.com/rust-lang/rust-clippy/pull/7326) + +### ICE Fixes + +* [`zero_sized_map_values`] + [#7470](https://github.com/rust-lang/rust-clippy/pull/7470) +* [`redundant_pattern_matching`] + [#7471](https://github.com/rust-lang/rust-clippy/pull/7471) +* [`modulo_one`] + [#7473](https://github.com/rust-lang/rust-clippy/pull/7473) +* [`use_self`] + [#7428](https://github.com/rust-lang/rust-clippy/pull/7428) + +### Documentation Improvements + +* Reworked Clippy's website: + [#7279](https://github.com/rust-lang/rust-clippy/pull/7279) + [#7172](https://github.com/rust-lang/rust-clippy/issues/7172) + * Added applicability information about lints + * Added a link to jump into the implementation + * Improved loading times + * Adapted some styling +* Clippy now uses a lint to generate its documentation + [#7298](https://github.com/rust-lang/rust-clippy/pull/7298) ## Rust 1.54 -Current beta, release 2021-07-29 +Current stable, released 2021-07-29 [7c7683c...3ae8faf](https://github.com/rust-lang/rust-clippy/compare/7c7683c...3ae8faf) @@ -29,7 +159,7 @@ Current beta, release 2021-07-29 ### Moves and Deprecations - Deprecate `pub_enum_variant_names` and `wrong_pub_self_convention` in favor of - the new `avoid_breaking_exported_api` config option (see + the new `avoid-breaking-exported-api` config option (see [Enhancements](#1-54-enhancements)) [#7187](https://github.com/rust-lang/rust-clippy/pull/7187) - Move [`inconsistent_struct_constructor`] to `pedantic` @@ -51,7 +181,7 @@ Current beta, release 2021-07-29 [#7163](https://github.com/rust-lang/rust-clippy/pull/7163) - [`if_then_some_else_none`]: Now works with the MSRV config [#7177](https://github.com/rust-lang/rust-clippy/pull/7177) -- Add `avoid_breaking_exported_api` config option for the lints +- Add `avoid-breaking-exported-api` config option for the lints [`enum_variant_names`], [`large_types_passed_by_value`], [`trivially_copy_pass_by_ref`], [`unnecessary_wraps`], [`upper_case_acronyms`], and [`wrong_self_convention`]. We recommend to set @@ -138,7 +268,7 @@ Current beta, release 2021-07-29 ## Rust 1.53 -Current stable, released 2021-06-17 +Released 2021-06-17 [6ed6f1e...7c7683c](https://github.com/rust-lang/rust-clippy/compare/6ed6f1e...7c7683c) @@ -2869,6 +2999,7 @@ Released 2018-09-13 [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit [`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings [`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result +[`unwrap_or_else_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_or_else_default [`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used [`upper_case_acronyms`]: https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms [`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug diff --git a/clippy_lints/src/as_conversions.rs b/clippy_lints/src/as_conversions.rs index 7c39a3e2ce3de..0be460d67a75f 100644 --- a/clippy_lints/src/as_conversions.rs +++ b/clippy_lints/src/as_conversions.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::ast::{Expr, ExprKind}; -use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -47,7 +47,7 @@ declare_lint_pass!(AsConversions => [AS_CONVERSIONS]); impl EarlyLintPass for AsConversions { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if in_external_macro(cx.sess(), expr.span) { + if in_external_macro(cx.sess, expr.span) { return; } diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index a3a3603c4c0ee..75561cfde369b 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -230,15 +230,7 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { panic_span: None, }; fpu.visit_expr(&body.value); - lint_for_missing_headers( - cx, - item.def_id, - item.span, - sig, - headers, - Some(body_id), - fpu.panic_span, - ); + lint_for_missing_headers(cx, item.def_id, item.span, sig, headers, Some(body_id), fpu.panic_span); } }, hir::ItemKind::Impl(ref impl_) => { @@ -278,15 +270,7 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { panic_span: None, }; fpu.visit_expr(&body.value); - lint_for_missing_headers( - cx, - item.def_id, - item.span, - sig, - headers, - Some(body_id), - fpu.panic_span, - ); + lint_for_missing_headers(cx, item.def_id, item.span, sig, headers, Some(body_id), fpu.panic_span); } } } diff --git a/clippy_lints/src/else_if_without_else.rs b/clippy_lints/src/else_if_without_else.rs index 0541ac5eccca4..b64246515f34f 100644 --- a/clippy_lints/src/else_if_without_else.rs +++ b/clippy_lints/src/else_if_without_else.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::ast::{Expr, ExprKind}; -use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -49,7 +49,7 @@ declare_lint_pass!(ElseIfWithoutElse => [ELSE_IF_WITHOUT_ELSE]); impl EarlyLintPass for ElseIfWithoutElse { fn check_expr(&mut self, cx: &EarlyContext<'_>, mut item: &Expr) { - if in_external_macro(cx.sess(), item.span) { + if in_external_macro(cx.sess, item.span) { return; } diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index ce23c0ce4a076..04fc5887e8e8b 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -251,7 +251,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions { hir_id: hir::HirId, ) { too_many_arguments::check_fn(cx, kind, decl, span, hir_id, self.too_many_arguments_threshold); - too_many_lines::check_fn(cx, span, body, self.too_many_lines_threshold); + too_many_lines::check_fn(cx, kind, span, body, self.too_many_lines_threshold); not_unsafe_ptr_arg_deref::check_fn(cx, kind, decl, body, hir_id); } diff --git a/clippy_lints/src/functions/too_many_lines.rs b/clippy_lints/src/functions/too_many_lines.rs index a666fee1a4ad5..008ef661b55f2 100644 --- a/clippy_lints/src/functions/too_many_lines.rs +++ b/clippy_lints/src/functions/too_many_lines.rs @@ -1,4 +1,5 @@ use rustc_hir as hir; +use rustc_hir::intravisit::FnKind; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_span::Span; @@ -8,8 +9,16 @@ use clippy_utils::source::snippet_opt; use super::TOO_MANY_LINES; -pub(super) fn check_fn(cx: &LateContext<'_>, span: Span, body: &'tcx hir::Body<'_>, too_many_lines_threshold: u64) { - if in_external_macro(cx.sess(), span) { +pub(super) fn check_fn( + cx: &LateContext<'_>, + kind: FnKind<'tcx>, + span: Span, + body: &'tcx hir::Body<'_>, + too_many_lines_threshold: u64, +) { + // Closures must be contained in a parent body, which will be checked for `too_many_lines`. + // Don't check closures for `too_many_lines` to avoid duplicated lints. + if matches!(kind, FnKind::Closure) || in_external_macro(cx.sess(), span) { return; } diff --git a/clippy_lints/src/if_not_else.rs b/clippy_lints/src/if_not_else.rs index 28db7233d70e8..3ce91d421baca 100644 --- a/clippy_lints/src/if_not_else.rs +++ b/clippy_lints/src/if_not_else.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::ast::{BinOpKind, Expr, ExprKind, UnOp}; -use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -48,7 +48,7 @@ declare_lint_pass!(IfNotElse => [IF_NOT_ELSE]); impl EarlyLintPass for IfNotElse { fn check_expr(&mut self, cx: &EarlyContext<'_>, item: &Expr) { - if in_external_macro(cx.sess(), item.span) { + if in_external_macro(cx.sess, item.span) { return; } if let ExprKind::If(ref cond, _, Some(ref els)) = item.kind { diff --git a/clippy_lints/src/items_after_statements.rs b/clippy_lints/src/items_after_statements.rs index 429c6ed7d2d77..3736d237642fb 100644 --- a/clippy_lints/src/items_after_statements.rs +++ b/clippy_lints/src/items_after_statements.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint; use rustc_ast::ast::{Block, ItemKind, StmtKind}; -use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -54,7 +54,7 @@ declare_lint_pass!(ItemsAfterStatements => [ITEMS_AFTER_STATEMENTS]); impl EarlyLintPass for ItemsAfterStatements { fn check_block(&mut self, cx: &EarlyContext<'_>, item: &Block) { - if in_external_macro(cx.sess(), item.span) { + if in_external_macro(cx.sess, item.span) { return; } @@ -68,7 +68,7 @@ impl EarlyLintPass for ItemsAfterStatements { // lint on all further items for stmt in stmts { if let StmtKind::Item(ref it) = *stmt { - if in_external_macro(cx.sess(), it.span) { + if in_external_macro(cx.sess, it.span) { return; } if let ItemKind::MacroDef(..) = it.kind { diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index a2cbfb1a05eea..a519ad90df54a 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -207,8 +207,7 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items } } - if cx.access_levels.is_exported(visited_trait.def_id) - && trait_items.iter().any(|i| is_named_self(cx, i, sym::len)) + if cx.access_levels.is_exported(visited_trait.def_id) && trait_items.iter().any(|i| is_named_self(cx, i, sym::len)) { let mut current_and_super_traits = DefIdSet::default(); fill_trait_set(visited_trait.def_id.to_def_id(), &mut current_and_super_traits, cx); @@ -331,17 +330,15 @@ fn check_for_is_empty( None, None, ), - Some(is_empty) if !cx.access_levels.is_exported(is_empty.def_id.expect_local()) => { - ( - format!( - "{} `{}` has a public `len` method, but a private `is_empty` method", - item_kind, - item_name.as_str(), - ), - Some(cx.tcx.def_span(is_empty.def_id)), - None, - ) - }, + Some(is_empty) if !cx.access_levels.is_exported(is_empty.def_id.expect_local()) => ( + format!( + "{} `{}` has a public `len` method, but a private `is_empty` method", + item_kind, + item_name.as_str(), + ), + Some(cx.tcx.def_span(is_empty.def_id)), + None, + ), Some(is_empty) if !(is_empty.fn_has_self_parameter && check_is_empty_sig(cx.tcx.fn_sig(is_empty.def_id).skip_binder(), self_kind, output)) => diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f49b382c5ea3b..dbdb4251b3bec 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -797,6 +797,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: methods::UNNECESSARY_FILTER_MAP, methods::UNNECESSARY_FOLD, methods::UNNECESSARY_LAZY_EVALUATIONS, + methods::UNWRAP_OR_ELSE_DEFAULT, methods::UNWRAP_USED, methods::USELESS_ASREF, methods::WRONG_SELF_CONVENTION, @@ -1341,6 +1342,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(methods::UNNECESSARY_FILTER_MAP), LintId::of(methods::UNNECESSARY_FOLD), LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS), + LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT), LintId::of(methods::USELESS_ASREF), LintId::of(methods::WRONG_SELF_CONVENTION), LintId::of(methods::ZST_OFFSET), @@ -1535,6 +1537,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(methods::STRING_EXTEND_CHARS), LintId::of(methods::UNNECESSARY_FOLD), LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS), + LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT), LintId::of(methods::WRONG_SELF_CONVENTION), LintId::of(misc::TOPLEVEL_REF_ARG), LintId::of(misc::ZERO_PTR), diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 699ddce0cff90..0e5121ca3d73a 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -10,7 +10,7 @@ use clippy_utils::{ use if_chain::if_chain; use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind}; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; use std::iter; @@ -222,7 +222,7 @@ impl_lint_pass!(LiteralDigitGrouping => [ impl EarlyLintPass for LiteralDigitGrouping { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if in_external_macro(cx.sess(), expr.span) { + if in_external_macro(cx.sess, expr.span) { return; } @@ -415,7 +415,7 @@ impl_lint_pass!(DecimalLiteralRepresentation => [DECIMAL_LITERAL_REPRESENTATION] impl EarlyLintPass for DecimalLiteralRepresentation { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if in_external_macro(cx.sess(), expr.span) { + if in_external_macro(cx.sess, expr.span) { return; } diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index e97b7c9417033..6d9f6215ed418 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -1,13 +1,36 @@ +use super::utils::make_iterator_snippet; use super::NEVER_LOOP; -use clippy_utils::diagnostics::span_lint; -use rustc_hir::{Block, Expr, ExprKind, HirId, InlineAsmOperand, Stmt, StmtKind}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::higher; +use clippy_utils::source::snippet; +use rustc_errors::Applicability; +use rustc_hir::{Block, Expr, ExprKind, HirId, InlineAsmOperand, LoopSource, Node, Pat, Stmt, StmtKind}; use rustc_lint::LateContext; use std::iter::{once, Iterator}; pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Loop(block, _, _, _) = expr.kind { + if let ExprKind::Loop(block, _, source, _) = expr.kind { match never_loop_block(block, expr.hir_id) { - NeverLoopResult::AlwaysBreak => span_lint(cx, NEVER_LOOP, expr.span, "this loop never actually loops"), + NeverLoopResult::AlwaysBreak => { + span_lint_and_then(cx, NEVER_LOOP, expr.span, "this loop never actually loops", |diag| { + if_chain! { + if let LoopSource::ForLoop = source; + if let Some((_, Node::Expr(parent_match))) = cx.tcx.hir().parent_iter(expr.hir_id).nth(1); + if let Some((pat, iterator, _, for_span)) = higher::for_loop(parent_match); + then { + // Suggests using an `if let` instead. This is `Unspecified` because the + // loop may (probably) contain `break` statements which would be invalid + // in an `if let`. + diag.span_suggestion_verbose( + for_span.with_hi(iterator.span.hi()), + "if you need the first element of the iterator, try writing", + for_to_if_let_sugg(cx, iterator, pat), + Applicability::Unspecified, + ); + } + }; + }); + }, NeverLoopResult::MayContinueMainLoop | NeverLoopResult::Otherwise => (), } } @@ -170,3 +193,14 @@ fn never_loop_expr_branch<'a, T: Iterator>>(e: &mut T, main_ e.map(|e| never_loop_expr(e, main_loop_id)) .fold(NeverLoopResult::AlwaysBreak, combine_branches) } + +fn for_to_if_let_sugg(cx: &LateContext<'_>, iterator: &Expr<'_>, pat: &Pat<'_>) -> String { + let pat_snippet = snippet(cx, pat.span, "_"); + let iter_snippet = make_iterator_snippet(cx, iterator, &mut Applicability::Unspecified); + + format!( + "if let Some({pat}) = {iter}.next()", + pat = pat_snippet, + iter = iter_snippet + ) +} diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index d57588716a5bf..ef822e0cbe540 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -7,7 +7,7 @@ use clippy_utils::{ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}; -use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, MatchSource, Node, PatKind, QPath, UnOp}; +use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, MatchSource, Mutability, Node, PatKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_span::{symbol::sym, Span, Symbol}; @@ -48,7 +48,12 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // borrowed mutably. TODO: If the struct can be partially moved from and the struct isn't used // afterwards a mutable borrow of a field isn't necessary. let ref_mut = if !iter_expr.fields.is_empty() || needs_mutable_borrow(cx, &iter_expr, loop_expr) { - "&mut " + if cx.typeck_results().node_type(iter_expr.hir_id).ref_mutability() == Some(Mutability::Mut) { + // Reborrow for mutable references. It may not be possible to get a mutable reference here. + "&mut *" + } else { + "&mut " + } } else { "" }; @@ -69,6 +74,8 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { struct IterExpr { /// The span of the whole expression, not just the path and fields stored here. span: Span, + /// The HIR id of the whole expression, not just the path and fields stored here. + hir_id: HirId, /// The fields used, in order of child to parent. fields: Vec, /// The path being used. @@ -78,12 +85,14 @@ struct IterExpr { /// the expression might have side effects. fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option { let span = e.span; + let hir_id = e.hir_id; let mut fields = Vec::new(); loop { match e.kind { ExprKind::Path(ref path) => { break Some(IterExpr { span, + hir_id, fields, path: cx.qpath_res(path, e.hir_id), }); @@ -137,7 +146,7 @@ fn is_expr_same_child_or_parent_field(cx: &LateContext<'_>, expr: &Expr<'_>, fie match expr.kind { ExprKind::Field(base, name) => { if let Some((head_field, tail_fields)) = fields.split_first() { - if name.name == *head_field && is_expr_same_field(cx, base, fields, path_res) { + if name.name == *head_field && is_expr_same_field(cx, base, tail_fields, path_res) { return true; } // Check if the expression is a parent field diff --git a/clippy_lints/src/methods/extend_with_drain.rs b/clippy_lints/src/methods/extend_with_drain.rs index 57e10ce42f8bb..8829b8c5f4df4 100644 --- a/clippy_lints/src/methods/extend_with_drain.rs +++ b/clippy_lints/src/methods/extend_with_drain.rs @@ -16,7 +16,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: //check source object if let ExprKind::MethodCall(src_method, _, [drain_vec, drain_arg], _) = &arg.kind; if src_method.ident.as_str() == "drain"; - if let src_ty = cx.typeck_results().expr_ty(drain_vec).peel_refs(); + let src_ty = cx.typeck_results().expr_ty(drain_vec); + //check if actual src type is mutable for code suggestion + let immutable = src_ty.is_mutable_ptr(); + let src_ty = src_ty.peel_refs(); if is_type_diagnostic_item(cx, src_ty, sym::vec_type); //check drain range if let src_ty_range = cx.typeck_results().expr_ty(drain_arg).peel_refs(); @@ -30,8 +33,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: "use of `extend` instead of `append` for adding the full range of a second vector", "try this", format!( - "{}.append(&mut {})", + "{}.append({}{})", snippet_with_applicability(cx, recv.span, "..", &mut applicability), + if immutable { "" } else { "&mut " }, snippet_with_applicability(cx, drain_vec.span, "..", &mut applicability) ), applicability, diff --git a/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/clippy_lints/src/methods/from_iter_instead_of_collect.rs index b4188d9ed3095..99c03844f4927 100644 --- a/clippy_lints/src/methods/from_iter_instead_of_collect.rs +++ b/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -1,10 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_opt; use clippy_utils::ty::implements_trait; use clippy_utils::{is_expr_path_def_path, paths, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_lint::{LateContext, LintContext}; +use rustc_lint::LateContext; use rustc_middle::ty::Ty; use rustc_span::sym; @@ -43,7 +44,7 @@ fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'tcx>) - let call_site = expr.span.source_callsite(); if_chain! { - if let Ok(snippet) = cx.sess().source_map().span_to_snippet(call_site); + if let Some(snippet) = snippet_opt(cx, call_site); let snippet_split = snippet.split("::").collect::>(); if let Some((_, elements)) = snippet_split.split_last(); diff --git a/clippy_lints/src/methods/map_flatten.rs b/clippy_lints/src/methods/map_flatten.rs index e8ad16bc0def9..08d3a7ce92bbe 100644 --- a/clippy_lints/src/methods/map_flatten.rs +++ b/clippy_lints/src/methods/map_flatten.rs @@ -52,18 +52,32 @@ pub(super) fn check<'tcx>( ); } - // lint if caller of `.map().flatten()` is an Option - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::option_type) { - let func_snippet = snippet(cx, map_arg.span, ".."); - let hint = format!(".and_then({})", func_snippet); - span_lint_and_sugg( - cx, - MAP_FLATTEN, - expr.span.with_lo(recv.span.hi()), - "called `map(..).flatten()` on an `Option`", - "try using `and_then` instead", - hint, - Applicability::MachineApplicable, - ); - } + // lint if caller of `.map().flatten()` is an Option or Result + let caller_type = match cx.typeck_results().expr_ty(recv).kind() { + ty::Adt(adt, _) => { + if cx.tcx.is_diagnostic_item(sym::option_type, adt.did) { + "Option" + } else if cx.tcx.is_diagnostic_item(sym::result_type, adt.did) { + "Result" + } else { + return; + } + }, + _ => { + return; + }, + }; + + let func_snippet = snippet(cx, map_arg.span, ".."); + let hint = format!(".and_then({})", func_snippet); + let lint_info = format!("called `map(..).flatten()` on an `{}`", caller_type); + span_lint_and_sugg( + cx, + MAP_FLATTEN, + expr.span.with_lo(recv.span.hi()), + &lint_info, + "try using `and_then` instead", + hint, + Applicability::MachineApplicable, + ); } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 1909fabb22fe7..91606ed3b2bb0 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -56,6 +56,7 @@ mod uninit_assumed_init; mod unnecessary_filter_map; mod unnecessary_fold; mod unnecessary_lazy_eval; +mod unwrap_or_else_default; mod unwrap_used; mod useless_asref; mod utils; @@ -310,6 +311,31 @@ declare_clippy_lint! { "using `ok().expect()`, which gives worse error messages than calling `expect` directly on the Result" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usages of `_.unwrap_or_else(Default::default)` on `Option` and + /// `Result` values. + /// + /// ### Why is this bad? + /// Readability, these can be written as `_.unwrap_or_default`, which is + /// simpler and more concise. + /// + /// ### Examples + /// ```rust + /// # let x = Some(1); + /// + /// // Bad + /// x.unwrap_or_else(Default::default); + /// x.unwrap_or_else(u32::default); + /// + /// // Good + /// x.unwrap_or_default(); + /// ``` + pub UNWRAP_OR_ELSE_DEFAULT, + style, + "using `.unwrap_or_else(Default::default)`, which is more succinctly expressed as `.unwrap_or_default()`" +} + declare_clippy_lint! { /// ### What it does /// Checks for usage of `option.map(_).unwrap_or(_)` or `option.map(_).unwrap_or_else(_)` or @@ -1766,6 +1792,7 @@ impl_lint_pass!(Methods => [ SHOULD_IMPLEMENT_TRAIT, WRONG_SELF_CONVENTION, OK_EXPECT, + UNWRAP_OR_ELSE_DEFAULT, MAP_UNWRAP_OR, RESULT_MAP_OR_INTO_OPTION, OPTION_MAP_OR_NONE, @@ -2172,7 +2199,10 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio }, ("unwrap_or_else", [u_arg]) => match method_call!(recv) { Some(("map", [recv, map_arg], _)) if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, msrv) => {}, - _ => unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or"), + _ => { + unwrap_or_else_default::check(cx, expr, recv, u_arg); + unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or"); + }, }, _ => {}, } diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index ef615b0aa40a8..c1d22e5d72c13 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -1,7 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::eager_or_lazy::is_lazyness_candidate; +use clippy_utils::is_trait_item; use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_macro_callsite}; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type}; +use clippy_utils::ty::implements_trait; +use clippy_utils::ty::{is_type_diagnostic_item, match_type}; use clippy_utils::{contains_return, last_path_segment, paths}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -34,15 +36,23 @@ pub(super) fn check<'tcx>( or_has_args: bool, span: Span, ) -> bool { + let is_default_default = || is_trait_item(cx, fun, sym::Default); + + let implements_default = |arg, default_trait_id| { + let arg_ty = cx.typeck_results().expr_ty(arg); + implements_trait(cx, arg_ty, default_trait_id, &[]) + }; + if_chain! { if !or_has_args; if name == "unwrap_or"; if let hir::ExprKind::Path(ref qpath) = fun.kind; - let path = last_path_segment(qpath).ident.name; - if matches!(path, kw::Default | sym::new); - let arg_ty = cx.typeck_results().expr_ty(arg); if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default); - if implements_trait(cx, arg_ty, default_trait_id, &[]); + let path = last_path_segment(qpath).ident.name; + // needs to target Default::default in particular or be *::new and have a Default impl + // available + if (matches!(path, kw::Default) && is_default_default()) + || (matches!(path, sym::new) && implements_default(arg, default_trait_id)); then { let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/methods/unwrap_or_else_default.rs b/clippy_lints/src/methods/unwrap_or_else_default.rs new file mode 100644 index 0000000000000..677aa80e1b76e --- /dev/null +++ b/clippy_lints/src/methods/unwrap_or_else_default.rs @@ -0,0 +1,45 @@ +//! Lint for `some_result_or_option.unwrap_or_else(Default::default)` + +use super::UNWRAP_OR_ELSE_DEFAULT; +use clippy_utils::{ + diagnostics::span_lint_and_sugg, is_trait_item, source::snippet_with_applicability, ty::is_type_diagnostic_item, +}; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_span::sym; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + recv: &'tcx hir::Expr<'_>, + u_arg: &'tcx hir::Expr<'_>, +) { + // something.unwrap_or_else(Default::default) + // ^^^^^^^^^- recv ^^^^^^^^^^^^^^^^- u_arg + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- expr + let recv_ty = cx.typeck_results().expr_ty(recv); + let is_option = is_type_diagnostic_item(cx, recv_ty, sym::option_type); + let is_result = is_type_diagnostic_item(cx, recv_ty, sym::result_type); + + if_chain! { + if is_option || is_result; + if is_trait_item(cx, u_arg, sym::Default); + then { + let mut applicability = Applicability::MachineApplicable; + + span_lint_and_sugg( + cx, + UNWRAP_OR_ELSE_DEFAULT, + expr.span, + "use of `.unwrap_or_else(..)` to construct default value", + "try", + format!( + "{}.unwrap_or_default()", + snippet_with_applicability(cx, recv.span, "..", &mut applicability) + ), + applicability, + ); + } + } +} diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index 06fe967dafc40..b32feab4ee3e7 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -12,7 +12,7 @@ use clippy_utils::source::snippet_opt; use rustc_ast::ast::{Expr, Generics, Lit, LitFloatType, LitIntType, LitKind, NodeId, Pat, PatKind}; use rustc_ast::visit::FnKind; use rustc_data_structures::fx::FxHashMap; -use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; @@ -307,7 +307,7 @@ impl EarlyLintPass for MiscEarlyLints { } fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if in_external_macro(cx.sess(), expr.span) { + if in_external_macro(cx.sess, expr.span) { return; } double_neg::check(cx, expr); diff --git a/clippy_lints/src/misc_early/unneeded_field_pattern.rs b/clippy_lints/src/misc_early/unneeded_field_pattern.rs index 2201cf56d52ab..fff533167ede2 100644 --- a/clippy_lints/src/misc_early/unneeded_field_pattern.rs +++ b/clippy_lints/src/misc_early/unneeded_field_pattern.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::source::snippet_opt; use rustc_ast::ast::{Pat, PatKind}; -use rustc_lint::{EarlyContext, LintContext}; +use rustc_lint::EarlyContext; use super::UNNEEDED_FIELD_PATTERN; @@ -48,7 +49,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { match field.pat.kind { PatKind::Wild => {}, _ => { - if let Ok(n) = cx.sess().source_map().span_to_snippet(field.span) { + if let Some(n) = snippet_opt(cx, field.span) { normal.push(n); } }, diff --git a/clippy_lints/src/needless_borrow.rs b/clippy_lints/src/needless_borrow.rs index 3f0b23ee4d3e4..ba8f9446af85e 100644 --- a/clippy_lints/src/needless_borrow.rs +++ b/clippy_lints/src/needless_borrow.rs @@ -27,11 +27,15 @@ declare_clippy_lint! { /// /// ### Example /// ```rust + /// fn fun(_a: &i32) {} + /// /// // Bad /// let x: &i32 = &&&&&&5; + /// fun(&x); /// /// // Good /// let x: &i32 = &5; + /// fun(x); /// ``` pub NEEDLESS_BORROW, style, diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs index 5088b8bb0d368..5a50cc48d61bf 100644 --- a/clippy_lints/src/needless_continue.rs +++ b/clippy_lints/src/needless_continue.rs @@ -422,7 +422,7 @@ fn check_and_warn<'a>(cx: &EarlyContext<'_>, expr: &'a ast::Expr) { /// /// is transformed to /// -/// ```ignore +/// ```text /// { /// let x = 5; /// ``` diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs index d9aa42fe8eeb6..9a6ddc72ce56a 100644 --- a/clippy_lints/src/needless_for_each.rs +++ b/clippy_lints/src/needless_for_each.rs @@ -122,7 +122,7 @@ impl LateLintPass<'_> for NeedlessForEach { /// 2. Detect use of `return` in `Loop` in the closure body. /// /// NOTE: The functionality of this type is similar to -/// [`crate::utilts::visitors::find_all_ret_expressions`], but we can't use +/// [`clippy_utils::visitors::find_all_ret_expressions`], but we can't use /// `find_all_ret_expressions` instead of this type. The reasons are: /// 1. `find_all_ret_expressions` passes the argument of `ExprKind::Ret` to a callback, but what we /// need here is `ExprKind::Ret` itself. diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index e07518b258687..28e9e6f438e3d 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -3,7 +3,7 @@ use clippy_utils::source::snippet_opt; use clippy_utils::ty::has_drop; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{BinOpKind, BlockCheckMode, Expr, ExprKind, Stmt, StmtKind, UnsafeSource}; +use rustc_hir::{is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, Stmt, StmtKind, UnsafeSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use std::ops::Deref; @@ -68,12 +68,14 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { ExprKind::Call(callee, args) => { if let ExprKind::Path(ref qpath) = callee.kind { let res = cx.qpath_res(qpath, callee.hir_id); - match res { - Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Ctor(..), ..) => { - !has_drop(cx, cx.typeck_results().expr_ty(expr)) - && args.iter().all(|arg| has_no_effect(cx, arg)) - }, - _ => false, + let def_matched = matches!( + res, + Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Ctor(..), ..) + ); + if def_matched || is_range_literal(expr) { + !has_drop(cx, cx.typeck_results().expr_ty(expr)) && args.iter().all(|arg| has_no_effect(cx, arg)) + } else { + false } } else { false diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 06c431babc23a..f6254aa715a45 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -127,6 +127,7 @@ const ALLOWED_TO_BE_SIMILAR: &[&[&str]] = &[ &["qpath", "path"], &["lit", "lint"], &["wparam", "lparam"], + &["iter", "item"], ]; struct SimilarNamesNameVisitor<'a, 'tcx, 'b>(&'b mut SimilarNamesLocalVisitor<'a, 'tcx>); diff --git a/clippy_lints/src/nonstandard_macro_braces.rs b/clippy_lints/src/nonstandard_macro_braces.rs index dbe9cbe0ded83..ca660a9250db1 100644 --- a/clippy_lints/src/nonstandard_macro_braces.rs +++ b/clippy_lints/src/nonstandard_macro_braces.rs @@ -7,6 +7,7 @@ use clippy_utils::{diagnostics::span_lint_and_help, in_macro, is_direct_expn_of, use if_chain::if_chain; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_hir::def_id::DefId; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; @@ -91,13 +92,23 @@ impl EarlyLintPass for MacroBraces { } fn is_offending_macro<'a>(cx: &EarlyContext<'_>, span: Span, mac_braces: &'a MacroBraces) -> Option> { + let unnested_or_local = || { + let nested = in_macro(span.ctxt().outer_expn_data().call_site); + !nested + || span + .macro_backtrace() + .last() + .map_or(false, |e| e.macro_def_id.map_or(false, DefId::is_local)) + }; if_chain! { + // Make sure we are only one level deep otherwise there are to many FP's if in_macro(span); if let Some((name, braces)) = find_matching_macro(span, &mac_braces.macro_braces); if let Some(snip) = snippet_opt(cx, span.ctxt().outer_expn_data().call_site); // we must check only invocation sites // https://github.com/rust-lang/rust-clippy/issues/7422 - if snip.starts_with(name); + if snip.starts_with(&format!("{}!", name)); + if unnested_or_local(); // make formatting consistent let c = snip.replace(" ", ""); if !c.starts_with(&format!("{}!{}", name, braces.0)); diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 1222a95d4eaa8..157b18c1f6b1f 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -2,9 +2,9 @@ use std::cmp; use std::iter; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_self_ty; use clippy_utils::source::snippet; use clippy_utils::ty::is_copy; +use clippy_utils::{is_self, is_self_ty}; use if_chain::if_chain; use rustc_ast::attr; use rustc_errors::Applicability; @@ -170,7 +170,7 @@ impl<'tcx> PassByRefOrValue { if size <= self.ref_min_size; if let hir::TyKind::Rptr(_, MutTy { ty: decl_ty, .. }) = input.kind; then { - let value_type = if is_self_ty(decl_ty) { + let value_type = if fn_body.and_then(|body| body.params.get(index)).map_or(false, is_self) { "self".into() } else { snippet(cx, decl_ty.span, "_").into() diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index a79b2fe76e2d5..7314bce83e038 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -8,7 +8,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit as hir_visit; use rustc_hir::intravisit::Visitor as HirVisitor; -use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -63,7 +63,7 @@ impl<'ast> ast_visit::Visitor<'ast> for ReturnVisitor { impl EarlyLintPass for RedundantClosureCall { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { - if in_external_macro(cx.sess(), expr.span) { + if in_external_macro(cx.sess, expr.span) { return; } if_chain! { diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index e0930d69ab9fe..77b6e60d89398 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -54,7 +54,8 @@ impl EarlyLintPass for DerefAddrOf { then { let mut applicability = Applicability::MachineApplicable; let sugg = if e.span.from_expansion() { - if let Ok(macro_source) = cx.sess.source_map().span_to_snippet(e.span) { + #[allow(clippy::option_if_let_else)] + if let Some(macro_source) = snippet_opt(cx, e.span) { // Remove leading whitespace from the given span // e.g: ` $visitor` turns into `$visitor` let trim_leading_whitespaces = |span| { diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 4fa8e77a67b78..f126908e84b03 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -1,15 +1,16 @@ -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{differing_macro_contexts, eq_expr_value}; +use clippy_utils::{can_mut_borrow_both, differing_macro_contexts, eq_expr_value}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, StmtKind}; +use rustc_hir::{BinOpKind, Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; +use rustc_span::source_map::Spanned; +use rustc_span::{sym, Span}; declare_clippy_lint! { /// ### What it does @@ -70,9 +71,67 @@ impl<'tcx> LateLintPass<'tcx> for Swap { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { check_manual_swap(cx, block); check_suspicious_swap(cx, block); + check_xor_swap(cx, block); } } +fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, span: Span, is_xor_based: bool) { + let mut applicability = Applicability::MachineApplicable; + + if !can_mut_borrow_both(cx, e1, e2) { + if let ExprKind::Index(lhs1, idx1) = e1.kind { + if let ExprKind::Index(lhs2, idx2) = e2.kind { + if eq_expr_value(cx, lhs1, lhs2) { + let ty = cx.typeck_results().expr_ty(lhs1).peel_refs(); + + if matches!(ty.kind(), ty::Slice(_)) + || matches!(ty.kind(), ty::Array(_, _)) + || is_type_diagnostic_item(cx, ty, sym::vec_type) + || is_type_diagnostic_item(cx, ty, sym::vecdeque_type) + { + let slice = Sugg::hir_with_applicability(cx, lhs1, "", &mut applicability); + span_lint_and_sugg( + cx, + MANUAL_SWAP, + span, + &format!("this looks like you are swapping elements of `{}` manually", slice), + "try", + format!( + "{}.swap({}, {})", + slice.maybe_par(), + snippet_with_applicability(cx, idx1.span, "..", &mut applicability), + snippet_with_applicability(cx, idx2.span, "..", &mut applicability), + ), + applicability, + ); + } + } + } + } + return; + } + + let first = Sugg::hir_with_applicability(cx, e1, "..", &mut applicability); + let second = Sugg::hir_with_applicability(cx, e2, "..", &mut applicability); + span_lint_and_then( + cx, + MANUAL_SWAP, + span, + &format!("this looks like you are swapping `{}` and `{}` manually", first, second), + |diag| { + diag.span_suggestion( + span, + "try", + format!("std::mem::swap({}, {})", first.mut_addr(), second.mut_addr()), + applicability, + ); + if !is_xor_based { + diag.note("or maybe you should use `std::mem::replace`?"); + } + }, + ); +} + /// Implementation of the `MANUAL_SWAP` lint. fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) { for w in block.stmts.windows(3) { @@ -96,121 +155,11 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) { if eq_expr_value(cx, tmp_init, lhs1); if eq_expr_value(cx, rhs1, lhs2); then { - if let ExprKind::Field(lhs1, _) = lhs1.kind { - if let ExprKind::Field(lhs2, _) = lhs2.kind { - if lhs1.hir_id.owner == lhs2.hir_id.owner { - return; - } - } - } - - let mut applicability = Applicability::MachineApplicable; - - let slice = check_for_slice(cx, lhs1, lhs2); - let (replace, what, sugg) = if let Slice::NotSwappable = slice { - return; - } else if let Slice::Swappable(slice, idx1, idx2) = slice { - if let Some(slice) = Sugg::hir_opt(cx, slice) { - ( - false, - format!(" elements of `{}`", slice), - format!( - "{}.swap({}, {})", - slice.maybe_par(), - snippet_with_applicability(cx, idx1.span, "..", &mut applicability), - snippet_with_applicability(cx, idx2.span, "..", &mut applicability), - ), - ) - } else { - (false, String::new(), String::new()) - } - } else if let (Some(first), Some(second)) = (Sugg::hir_opt(cx, lhs1), Sugg::hir_opt(cx, rhs1)) { - ( - true, - format!(" `{}` and `{}`", first, second), - format!("std::mem::swap({}, {})", first.mut_addr(), second.mut_addr()), - ) - } else { - (true, String::new(), String::new()) - }; - let span = w[0].span.to(second.span); - - span_lint_and_then( - cx, - MANUAL_SWAP, - span, - &format!("this looks like you are swapping{} manually", what), - |diag| { - if !sugg.is_empty() { - diag.span_suggestion( - span, - "try", - sugg, - applicability, - ); - - if replace { - diag.note("or maybe you should use `std::mem::replace`?"); - } - } - } - ); - } - } - } -} - -enum Slice<'a> { - /// `slice.swap(idx1, idx2)` can be used - /// - /// ## Example - /// - /// ```rust - /// # let mut a = vec![0, 1]; - /// let t = a[1]; - /// a[1] = a[0]; - /// a[0] = t; - /// // can be written as - /// a.swap(0, 1); - /// ``` - Swappable(&'a Expr<'a>, &'a Expr<'a>, &'a Expr<'a>), - /// The `swap` function cannot be used. - /// - /// ## Example - /// - /// ```rust - /// # let mut a = [vec![1, 2], vec![3, 4]]; - /// let t = a[0][1]; - /// a[0][1] = a[1][0]; - /// a[1][0] = t; - /// ``` - NotSwappable, - /// Not a slice - None, -} - -/// Checks if both expressions are index operations into "slice-like" types. -fn check_for_slice<'a>(cx: &LateContext<'_>, lhs1: &'a Expr<'_>, lhs2: &'a Expr<'_>) -> Slice<'a> { - if let ExprKind::Index(lhs1, idx1) = lhs1.kind { - if let ExprKind::Index(lhs2, idx2) = lhs2.kind { - if eq_expr_value(cx, lhs1, lhs2) { - let ty = cx.typeck_results().expr_ty(lhs1).peel_refs(); - - if matches!(ty.kind(), ty::Slice(_)) - || matches!(ty.kind(), ty::Array(_, _)) - || is_type_diagnostic_item(cx, ty, sym::vec_type) - || is_type_diagnostic_item(cx, ty, sym::vecdeque_type) - { - return Slice::Swappable(lhs1, idx1, idx2); - } - } else { - return Slice::NotSwappable; + generate_swap_warning(cx, lhs1, lhs2, span, false); } } } - - Slice::None } /// Implementation of the `ALMOST_SWAPPED` lint. @@ -262,3 +211,40 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) { } } } + +/// Implementation of the xor case for `MANUAL_SWAP` lint. +fn check_xor_swap(cx: &LateContext<'_>, block: &Block<'_>) { + for window in block.stmts.windows(3) { + if_chain! { + if let Some((lhs0, rhs0)) = extract_sides_of_xor_assign(&window[0]); + if let Some((lhs1, rhs1)) = extract_sides_of_xor_assign(&window[1]); + if let Some((lhs2, rhs2)) = extract_sides_of_xor_assign(&window[2]); + if eq_expr_value(cx, lhs0, rhs1); + if eq_expr_value(cx, lhs2, rhs1); + if eq_expr_value(cx, lhs1, rhs0); + if eq_expr_value(cx, lhs1, rhs2); + then { + let span = window[0].span.to(window[2].span); + generate_swap_warning(cx, lhs0, rhs0, span, true); + } + }; + } +} + +/// Returns the lhs and rhs of an xor assignment statement. +fn extract_sides_of_xor_assign<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(&'a Expr<'hir>, &'a Expr<'hir>)> { + if let StmtKind::Semi(expr) = stmt.kind { + if let ExprKind::AssignOp( + Spanned { + node: BinOpKind::BitXor, + .. + }, + lhs, + rhs, + ) = expr.kind + { + return Some((lhs, rhs)); + } + } + None +} diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index ad7409fe3a9b7..371bb8b445a71 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -186,7 +186,7 @@ declare_clippy_lint! { /// Checks for use of redundant allocations anywhere in the code. /// /// ### Why is this bad? - /// Expressions such as `Rc<&T>`, `Rc>`, `Rc>`, `Rc>`, Arc<&T>`, `Arc>`, + /// Expressions such as `Rc<&T>`, `Rc>`, `Rc>`, `Rc>`, `Arc<&T>`, `Arc>`, /// `Arc>`, `Arc>`, `Box<&T>`, `Box>`, `Box>`, `Box>`, add an unnecessary level of indirection. /// /// ### Example diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index 9acfbc994b382..c8a231341b7e1 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -35,8 +35,6 @@ declare_clippy_lint! { /// ``` /// Use instead: /// ```rust - /// #![feature(or_patterns)] - /// /// fn main() { /// if let Some(0 | 2) = Some(0) {} /// } diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index 9ed5e585f841d..1164ac4938fb6 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::position_before_rarrow; +use clippy_utils::source::{position_before_rarrow, snippet_opt}; use if_chain::if_chain; use rustc_ast::ast; use rustc_ast::visit::FnKind; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::BytePos; @@ -125,17 +125,16 @@ fn is_unit_expr(expr: &ast::Expr) -> bool { } fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { - let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) { - position_before_rarrow(&fn_source).map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { - ( - #[allow(clippy::cast_possible_truncation)] - ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), - Applicability::MachineApplicable, - ) - }) - } else { - (ty.span, Applicability::MaybeIncorrect) - }; + let (ret_span, appl) = + snippet_opt(cx, span.with_hi(ty.span.hi())).map_or((ty.span, Applicability::MaybeIncorrect), |fn_source| { + position_before_rarrow(&fn_source).map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { + ( + #[allow(clippy::cast_possible_truncation)] + ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), + Applicability::MachineApplicable, + ) + }) + }); span_lint_and_sugg( cx, UNUSED_UNIT, diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 44d3d4563428d..a28b1d78f7d46 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -36,13 +36,13 @@ impl TryConf { /// See (rust-clippy#7172) macro_rules! define_Conf { ($( - #[doc = $doc:literal] + $(#[doc = $doc:literal])+ $(#[conf_deprecated($dep:literal)])? ($name:ident: $ty:ty = $default:expr), )*) => { /// Clippy lint configuration pub struct Conf { - $(#[doc = $doc] pub $name: $ty,)* + $($(#[doc = $doc])+ pub $name: $ty,)* } mod defaults { @@ -119,7 +119,7 @@ macro_rules! define_Conf { stringify!($name), stringify!($ty), format!("{:?}", super::defaults::$name()), - $doc, + concat!($($doc, '\n',)*), deprecation_reason, ) }, @@ -132,18 +132,30 @@ macro_rules! define_Conf { // N.B., this macro is parsed by util/lintlib.py define_Conf! { - /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION. Suppress lints whenever the suggested change would cause breakage for other crates. + /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION. + /// + /// Suppress lints whenever the suggested change would cause breakage for other crates. (avoid_breaking_exported_api: bool = true), - /// Lint: MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE. The minimum rust version that the project supports + /// Lint: MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE. + /// + /// The minimum rust version that the project supports (msrv: Option = None), - /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses + /// Lint: BLACKLISTED_NAME. + /// + /// The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses (blacklisted_names: Vec = ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()), - /// Lint: COGNITIVE_COMPLEXITY. The maximum cognitive complexity a function can have + /// Lint: COGNITIVE_COMPLEXITY. + /// + /// The maximum cognitive complexity a function can have (cognitive_complexity_threshold: u64 = 25), - /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. Use the Cognitive Complexity lint instead. + /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. + /// + /// Use the Cognitive Complexity lint instead. #[conf_deprecated("Please use `cognitive-complexity-threshold` instead")] (cyclomatic_complexity_threshold: Option = None), - /// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks + /// Lint: DOC_MARKDOWN. + /// + /// The list of words this lint should not consider as identifiers needing ticks (doc_valid_idents: Vec = [ "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", @@ -164,55 +176,109 @@ define_Conf! { "MinGW", "CamelCase", ].iter().map(ToString::to_string).collect()), - /// Lint: TOO_MANY_ARGUMENTS. The maximum number of argument a function or method can have + /// Lint: TOO_MANY_ARGUMENTS. + /// + /// The maximum number of argument a function or method can have (too_many_arguments_threshold: u64 = 7), - /// Lint: TYPE_COMPLEXITY. The maximum complexity a type can have + /// Lint: TYPE_COMPLEXITY. + /// + /// The maximum complexity a type can have (type_complexity_threshold: u64 = 250), - /// Lint: MANY_SINGLE_CHAR_NAMES. The maximum number of single char bindings a scope may have + /// Lint: MANY_SINGLE_CHAR_NAMES. + /// + /// The maximum number of single char bindings a scope may have (single_char_binding_names_threshold: u64 = 4), - /// Lint: BOXED_LOCAL, USELESS_VEC. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap + /// Lint: BOXED_LOCAL, USELESS_VEC. + /// + /// The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap (too_large_for_stack: u64 = 200), - /// Lint: ENUM_VARIANT_NAMES. The minimum number of enum variants for the lints about variant names to trigger + /// Lint: ENUM_VARIANT_NAMES. + /// + /// The minimum number of enum variants for the lints about variant names to trigger (enum_variant_name_threshold: u64 = 3), - /// Lint: LARGE_ENUM_VARIANT. The maximum size of a enum's variant to avoid box suggestion + /// Lint: LARGE_ENUM_VARIANT. + /// + /// The maximum size of a enum's variant to avoid box suggestion (enum_variant_size_threshold: u64 = 200), - /// Lint: VERBOSE_BIT_MASK. The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros' + /// Lint: VERBOSE_BIT_MASK. + /// + /// The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros' (verbose_bit_mask_threshold: u64 = 1), - /// Lint: DECIMAL_LITERAL_REPRESENTATION. The lower bound for linting decimal literals + /// Lint: DECIMAL_LITERAL_REPRESENTATION. + /// + /// The lower bound for linting decimal literals (literal_representation_threshold: u64 = 16384), - /// Lint: TRIVIALLY_COPY_PASS_BY_REF. The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference. + /// Lint: TRIVIALLY_COPY_PASS_BY_REF. + /// + /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference. (trivial_copy_size_limit: Option = None), - /// Lint: LARGE_TYPE_PASS_BY_MOVE. The minimum size (in bytes) to consider a type for passing by reference instead of by value. + /// Lint: LARGE_TYPE_PASS_BY_MOVE. + /// + /// The minimum size (in bytes) to consider a type for passing by reference instead of by value. (pass_by_value_size_limit: u64 = 256), - /// Lint: TOO_MANY_LINES. The maximum number of lines a function or method can have + /// Lint: TOO_MANY_LINES. + /// + /// The maximum number of lines a function or method can have (too_many_lines_threshold: u64 = 100), - /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. The maximum allowed size for arrays on the stack + /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. + /// + /// The maximum allowed size for arrays on the stack (array_size_threshold: u64 = 512_000), - /// Lint: VEC_BOX. The size of the boxed type in bytes, where boxing in a `Vec` is allowed + /// Lint: VEC_BOX. + /// + /// The size of the boxed type in bytes, where boxing in a `Vec` is allowed (vec_box_size_threshold: u64 = 4096), - /// Lint: TYPE_REPETITION_IN_BOUNDS. The maximum number of bounds a trait can have to be linted + /// Lint: TYPE_REPETITION_IN_BOUNDS. + /// + /// The maximum number of bounds a trait can have to be linted (max_trait_bounds: u64 = 3), - /// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bool fields a struct can have + /// Lint: STRUCT_EXCESSIVE_BOOLS. + /// + /// The maximum number of bool fields a struct can have (max_struct_bools: u64 = 3), - /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bool parameters a function can have + /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. + /// + /// The maximum number of bool parameters a function can have (max_fn_params_bools: u64 = 3), - /// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests). + /// Lint: WILDCARD_IMPORTS. + /// + /// Whether to allow certain wildcard imports (prelude, super in tests). (warn_on_all_wildcard_imports: bool = false), - /// Lint: DISALLOWED_METHOD. The list of disallowed methods, written as fully qualified paths. + /// Lint: DISALLOWED_METHOD. + /// + /// The list of disallowed methods, written as fully qualified paths. (disallowed_methods: Vec = Vec::new()), - /// Lint: DISALLOWED_TYPE. The list of disallowed types, written as fully qualified paths. + /// Lint: DISALLOWED_TYPE. + /// + /// The list of disallowed types, written as fully qualified paths. (disallowed_types: Vec = Vec::new()), - /// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators. + /// Lint: UNREADABLE_LITERAL. + /// + /// Should the fraction of a decimal be linted to include separators. (unreadable_literal_lint_fractions: bool = true), - /// Lint: UPPER_CASE_ACRONYMS. Enables verbose mode. Triggers if there is more than one uppercase char next to each other + /// Lint: UPPER_CASE_ACRONYMS. + /// + /// Enables verbose mode. Triggers if there is more than one uppercase char next to each other (upper_case_acronyms_aggressive: bool = false), - /// Lint: _CARGO_COMMON_METADATA. For internal testing only, ignores the current `publish` settings in the Cargo manifest. + /// Lint: _CARGO_COMMON_METADATA. + /// + /// For internal testing only, ignores the current `publish` settings in the Cargo manifest. (cargo_ignore_publish: bool = false), - /// Lint: NONSTANDARD_MACRO_BRACES. Enforce the named macros always use the braces specified.
A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro is could be used with a full path two `MacroMatcher`s have to be added one with the full path `crate_name::macro_name` and one with just the macro name. + /// Lint: NONSTANDARD_MACRO_BRACES. + /// + /// Enforce the named macros always use the braces specified. + /// + /// A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro + /// is could be used with a full path two `MacroMatcher`s have to be added one with the full path + /// `crate_name::macro_name` and one with just the macro name. (standard_macro_braces: Vec = Vec::new()), - /// Lint: MISSING_ENFORCED_IMPORT_RENAMES. The list of imports to always rename, a fully qualified path followed by the rename. + /// Lint: MISSING_ENFORCED_IMPORT_RENAMES. + /// + /// The list of imports to always rename, a fully qualified path followed by the rename. (enforced_import_renames: Vec = Vec::new()), - /// Lint: RESTRICTED_SCRIPTS. The list of unicode scripts allowed to be used in the scope. + /// Lint: RESTRICTED_SCRIPTS. + /// + /// The list of unicode scripts allowed to be used in the scope. (allowed_scripts: Vec = vec!["Latin".to_string()]), } diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 47336459d7da6..a48a53850830f 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -82,7 +82,7 @@ This lint has the following configuration variables: /// `default` macro_rules! CONFIGURATION_VALUE_TEMPLATE { () => { - "* {name}: {ty}: {doc} (defaults to `{default}`)\n" + "* {name}: `{ty}`: {doc} (defaults to `{default}`)\n" }; } @@ -344,11 +344,16 @@ fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec, String)> { if let Some(split_pos) = doc_comment.find('.'); then { let mut doc_comment = doc_comment.to_string(); - let documentation = doc_comment.split_off(split_pos); + let mut documentation = doc_comment.split_off(split_pos); + // Extract lints doc_comment.make_ascii_lowercase(); let lints: Vec = doc_comment.split_off(DOC_START.len()).split(", ").map(str::to_string).collect(); + // Format documentation correctly + // split off leading `.` from lint name list and indent for correct formatting + documentation = documentation.trim_start_matches('.').trim().replace("\n ", "\n "); + Some((lints, documentation)) } else { None diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index 7c94474cb35d2..71cfa196fc335 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -65,7 +65,7 @@ pub fn span_lint(cx: &T, lint: &'static Lint, sp: impl Into $DIR/zero_div_zero.rs:6:25 /// | @@ -103,7 +103,7 @@ pub fn span_lint_and_help<'a, T: LintContext>( /// /// # Example /// -/// ```ignore +/// ```text /// error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. /// --> $DIR/drop_forget_ref.rs:10:5 /// | @@ -189,7 +189,7 @@ pub fn span_lint_hir_and_then( /// /// # Example /// -/// ```ignore +/// ```text /// error: This `.fold` can be more succinctly expressed as `.any` /// --> $DIR/methods.rs:390:13 /// | diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index f32f1109b08e6..884180f0586e3 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -195,8 +195,8 @@ pub fn vec_macro<'e>(cx: &LateContext<'_>, expr: &'e hir::Expr<'_>) -> Option(e: &'tcx Expr<'tcx>) -> Option>> { /// Try to match the AST for a pattern that contains a match, for example when two args are /// compared @@ -283,7 +283,7 @@ pub struct FormatArgsExpn<'tcx> { /// String literal expressions which represent the format string split by "{}" pub format_string_parts: &'tcx [Expr<'tcx>], - /// Symbols corresponding to [`format_string_parts`] + /// Symbols corresponding to [`Self::format_string_parts`] pub format_string_symbols: Vec, /// Expressions like `ArgumentV1::new(arg0, Debug::fmt)` pub args: &'tcx [Expr<'tcx>], diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 59f878f8b20ab..1d59d6bfea1b9 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -326,6 +326,25 @@ pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) .map_or(false, |did| is_diag_trait_item(cx, did, diag_item)) } +/// Checks if the given expression is a path referring an item on the trait +/// that is marked with the given diagnostic item. +/// +/// For checking method call expressions instead of path expressions, use +/// [`is_trait_method`]. +/// +/// For example, this can be used to find if an expression like `u64::default` +/// refers to an item of the trait `Default`, which is associated with the +/// `diag_item` of `sym::Default`. +pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool { + if let hir::ExprKind::Path(ref qpath) = expr.kind { + cx.qpath_res(qpath, expr.hir_id) + .opt_def_id() + .map_or(false, |def_id| is_diag_trait_item(cx, def_id, diag_item)) + } else { + false + } +} + pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> { match *path { QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"), @@ -558,6 +577,54 @@ pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio None } +/// This method will return tuple of projection stack and root of the expression, +/// used in `can_mut_borrow_both`. +/// +/// For example, if `e` represents the `v[0].a.b[x]` +/// this method will return a tuple, composed of a `Vec` +/// containing the `Expr`s for `v[0], v[0].a, v[0].a.b, v[0].a.b[x]` +/// and a `Expr` for root of them, `v` +fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) { + let mut result = vec![]; + let root = loop { + match e.kind { + ExprKind::Index(ep, _) | ExprKind::Field(ep, _) => { + result.push(e); + e = ep; + }, + _ => break e, + }; + }; + result.reverse(); + (result, root) +} + +/// Checks if two expressions can be mutably borrowed simultaneously +/// and they aren't dependent on borrowing same thing twice +pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool { + let (s1, r1) = projection_stack(e1); + let (s2, r2) = projection_stack(e2); + if !eq_expr_value(cx, r1, r2) { + return true; + } + for (x1, x2) in s1.iter().zip(s2.iter()) { + match (&x1.kind, &x2.kind) { + (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => { + if i1 != i2 { + return true; + } + }, + (ExprKind::Index(_, i1), ExprKind::Index(_, i2)) => { + if !eq_expr_value(cx, i1, i2) { + return false; + } + }, + _ => return false, + } + } + false +} + /// Checks if the top level expression can be moved into a closure as is. pub fn can_move_expr_to_closure_no_visit(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, jump_targets: &[HirId]) -> bool { match expr.kind { diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 4d49b43bde9ec..789079510c5e8 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -168,7 +168,7 @@ pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow< snippet_opt(cx, span).map_or_else(|| Cow::Borrowed(default), From::from) } -/// Same as `snippet`, but it adapts the applicability level by following rules: +/// Same as [`snippet`], but it adapts the applicability level by following rules: /// /// - Applicability level `Unspecified` will never be changed. /// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`. diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index e914dc1c222f6..4f9aaf396b806 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -114,7 +114,7 @@ pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option< /// Checks whether a type implements a trait. /// The function returns false in case the type contains an inference variable. -/// See also `get_trait_def_id`. +/// See also [`get_trait_def_id`](super::get_trait_def_id). pub fn implements_trait<'tcx>( cx: &LateContext<'tcx>, ty: Ty<'tcx>, diff --git a/doc/basics.md b/doc/basics.md index 43d3792f59521..ff2e0417435bf 100644 --- a/doc/basics.md +++ b/doc/basics.md @@ -166,8 +166,8 @@ rustup component add clippy ``` > **DO NOT** install using `cargo install --path . --force` since this will overwrite rustup -[proxies](https://rust-lang.github.io/rustup/concepts/proxies.html). That is, `~/.cargo/bin/cargo-clippy` and -`~/.cargo/bin/clippy-driver` should be hard or soft links to `~/.cargo/bin/rustup`. You can repair these by running -`rustup update`. +> [proxies](https://rust-lang.github.io/rustup/concepts/proxies.html). That is, `~/.cargo/bin/cargo-clippy` and +> `~/.cargo/bin/clippy-driver` should be hard or soft links to `~/.cargo/bin/rustup`. You can repair these by running +> `rustup update`. [glossary]: https://rustc-dev-guide.rust-lang.org/appendix/glossary.html diff --git a/rust-toolchain b/rust-toolchain index bff657bc11038..23887f1784549 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-07-29" +channel = "nightly-2021-08-12" components = ["llvm-tools-preview", "rustc-dev", "rust-src"] diff --git a/tests/ui-toml/functions_maxlines/test.rs b/tests/ui-toml/functions_maxlines/test.rs index a47677a1f3a2e..33a3ef7513631 100644 --- a/tests/ui-toml/functions_maxlines/test.rs +++ b/tests/ui-toml/functions_maxlines/test.rs @@ -1,3 +1,5 @@ +// edition:2018 + #![warn(clippy::too_many_lines)] // This function should be considered one line. @@ -20,6 +22,20 @@ fn too_many_lines() { println!("This is bad."); } +// This should only fail once (#7517). +async fn async_too_many_lines() { + println!("This is bad."); + println!("This is bad."); +} + +// This should fail only once, without failing on the closure. +fn closure_too_many_lines() { + let _ = { + println!("This is bad."); + println!("This is bad."); + }; +} + // This should be considered one line. #[rustfmt::skip] fn comment_starts_after_code() { diff --git a/tests/ui-toml/functions_maxlines/test.stderr b/tests/ui-toml/functions_maxlines/test.stderr index a27ce945ca584..7551cac9f504b 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:20:1 | LL | / fn too_many_lines() { LL | | println!("This is bad."); @@ -9,8 +9,28 @@ LL | | } | = note: `-D clippy::too-many-lines` implied by `-D warnings` +error: this function has too many lines (4/1) + --> $DIR/test.rs:26:1 + | +LL | / async fn async_too_many_lines() { +LL | | println!("This is bad."); +LL | | println!("This is bad."); +LL | | } + | |_^ + +error: this function has too many lines (4/1) + --> $DIR/test.rs:32:1 + | +LL | / fn closure_too_many_lines() { +LL | | let _ = { +LL | | println!("This is bad."); +LL | | println!("This is bad."); +LL | | }; +LL | | } + | |_^ + error: this function has too many lines (2/1) - --> $DIR/test.rs:38:1 + --> $DIR/test.rs:54:1 | LL | / fn comment_before_code() { LL | | let _ = "test"; @@ -19,5 +39,5 @@ LL | | the code but this line should still count. */ let _ = 5; LL | | } | |_^ -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs index e9f042ddefcde..5b4adc868dff1 100644 --- a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs @@ -32,13 +32,19 @@ macro_rules! type_pos { }; } +macro_rules! printlnfoo { + ($thing:expr) => { + println!("{}", $thing) + }; +} + #[rustfmt::skip] fn main() { let _ = vec! {1, 2, 3}; let _ = format!["ugh {} stop being such a good compiler", "hello"]; let _ = quote!(let x = 1;); let _ = quote::quote!(match match match); - let _ = test!(); + let _ = test!(); // trigger when macro def is inside our own crate let _ = vec![1,2,3]; let _ = quote::quote! {true || false}; @@ -49,4 +55,6 @@ fn main() { let _: type_pos!(usize) = vec![]; eprint!("test if user config overrides defaults"); + + printlnfoo!["test if printlnfoo is triggered by println"]; } diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr index 86063a0828086..87e962b9228c4 100644 --- a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr @@ -1,48 +1,48 @@ error: use of irregular braces for `vec!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:37:13 + --> $DIR/conf_nonstandard_macro_braces.rs:43:13 | LL | let _ = vec! {1, 2, 3}; | ^^^^^^^^^^^^^^ | = note: `-D clippy::nonstandard-macro-braces` implied by `-D warnings` help: consider writing `vec![1, 2, 3]` - --> $DIR/conf_nonstandard_macro_braces.rs:37:13 + --> $DIR/conf_nonstandard_macro_braces.rs:43:13 | LL | let _ = vec! {1, 2, 3}; | ^^^^^^^^^^^^^^ error: use of irregular braces for `format!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:38:13 + --> $DIR/conf_nonstandard_macro_braces.rs:44:13 | LL | let _ = format!["ugh {} stop being such a good compiler", "hello"]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: consider writing `format!("ugh () stop being such a good compiler", "hello")` - --> $DIR/conf_nonstandard_macro_braces.rs:38:13 + --> $DIR/conf_nonstandard_macro_braces.rs:44:13 | LL | let _ = format!["ugh {} stop being such a good compiler", "hello"]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of irregular braces for `quote!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:39:13 + --> $DIR/conf_nonstandard_macro_braces.rs:45:13 | LL | let _ = quote!(let x = 1;); | ^^^^^^^^^^^^^^^^^^ | help: consider writing `quote! {let x = 1;}` - --> $DIR/conf_nonstandard_macro_braces.rs:39:13 + --> $DIR/conf_nonstandard_macro_braces.rs:45:13 | LL | let _ = quote!(let x = 1;); | ^^^^^^^^^^^^^^^^^^ error: use of irregular braces for `quote::quote!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:40:13 + --> $DIR/conf_nonstandard_macro_braces.rs:46:13 | LL | let _ = quote::quote!(match match match); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: consider writing `quote::quote! {match match match}` - --> $DIR/conf_nonstandard_macro_braces.rs:40:13 + --> $DIR/conf_nonstandard_macro_braces.rs:46:13 | LL | let _ = quote::quote!(match match match); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -53,7 +53,7 @@ error: use of irregular braces for `vec!` macro LL | vec!{0, 0, 0} | ^^^^^^^^^^^^^ ... -LL | let _ = test!(); +LL | let _ = test!(); // trigger when macro def is inside our own crate | ------- in this macro invocation | help: consider writing `vec![0, 0, 0]` @@ -62,30 +62,30 @@ help: consider writing `vec![0, 0, 0]` LL | vec!{0, 0, 0} | ^^^^^^^^^^^^^ ... -LL | let _ = test!(); +LL | let _ = test!(); // trigger when macro def is inside our own crate | ------- in this macro invocation = note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) error: use of irregular braces for `type_pos!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:49:12 + --> $DIR/conf_nonstandard_macro_braces.rs:55:12 | LL | let _: type_pos!(usize) = vec![]; | ^^^^^^^^^^^^^^^^ | help: consider writing `type_pos![usize]` - --> $DIR/conf_nonstandard_macro_braces.rs:49:12 + --> $DIR/conf_nonstandard_macro_braces.rs:55:12 | LL | let _: type_pos!(usize) = vec![]; | ^^^^^^^^^^^^^^^^ error: use of irregular braces for `eprint!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:51:5 + --> $DIR/conf_nonstandard_macro_braces.rs:57:5 | LL | eprint!("test if user config overrides defaults"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: consider writing `eprint!["test if user config overrides defaults"];` - --> $DIR/conf_nonstandard_macro_braces.rs:51:5 + --> $DIR/conf_nonstandard_macro_braces.rs:57:5 | LL | eprint!("test if user config overrides defaults"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/auxiliary/test_macro.rs b/tests/ui/auxiliary/test_macro.rs similarity index 100% rename from tests/auxiliary/test_macro.rs rename to tests/ui/auxiliary/test_macro.rs diff --git a/tests/ui/extend_with_drain.fixed b/tests/ui/extend_with_drain.fixed index 00170e649e251..e863870e7d61b 100644 --- a/tests/ui/extend_with_drain.fixed +++ b/tests/ui/extend_with_drain.fixed @@ -41,7 +41,12 @@ fn main() { let mut heap = BinaryHeap::from(vec![1, 3]); let mut heap2 = BinaryHeap::from(vec![]); - heap2.extend(heap.drain()) + heap2.extend(heap.drain()); + + let mut x = vec![0, 1, 2, 3, 5]; + let ref_x = &mut x; + let mut y = Vec::new(); + y.append(ref_x); } fn return_vector() -> Vec { diff --git a/tests/ui/extend_with_drain.rs b/tests/ui/extend_with_drain.rs index d76458c32891a..dcb36b5951cb2 100644 --- a/tests/ui/extend_with_drain.rs +++ b/tests/ui/extend_with_drain.rs @@ -41,7 +41,12 @@ fn main() { let mut heap = BinaryHeap::from(vec![1, 3]); let mut heap2 = BinaryHeap::from(vec![]); - heap2.extend(heap.drain()) + heap2.extend(heap.drain()); + + let mut x = vec![0, 1, 2, 3, 5]; + let ref_x = &mut x; + let mut y = Vec::new(); + y.extend(ref_x.drain(..)); } fn return_vector() -> Vec { diff --git a/tests/ui/extend_with_drain.stderr b/tests/ui/extend_with_drain.stderr index 57f344716a16d..da14ddb25b373 100644 --- a/tests/ui/extend_with_drain.stderr +++ b/tests/ui/extend_with_drain.stderr @@ -18,5 +18,11 @@ error: use of `extend` instead of `append` for adding the full range of a second LL | vec11.extend(return_vector().drain(..)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec11.append(&mut return_vector())` -error: aborting due to 3 previous errors +error: use of `extend` instead of `append` for adding the full range of a second vector + --> $DIR/extend_with_drain.rs:49:5 + | +LL | y.extend(ref_x.drain(..)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `y.append(ref_x)` + +error: aborting due to 4 previous errors diff --git a/tests/ui/implicit_hasher.rs b/tests/ui/implicit_hasher.rs index fdcc9a33f55fe..97c26bc83ad4b 100644 --- a/tests/ui/implicit_hasher.rs +++ b/tests/ui/implicit_hasher.rs @@ -89,7 +89,7 @@ gen!(fn bar); // and should not cause an ICE // See #2707 #[macro_use] -#[path = "../auxiliary/test_macro.rs"] +#[path = "auxiliary/test_macro.rs"] pub mod test_macro; __implicit_hasher_test_macro!(impl for HashMap where V: test_macro::A); diff --git a/tests/ui/map_flatten.fixed b/tests/ui/map_flatten.fixed index 773b5914439d3..18846c898da05 100644 --- a/tests/ui/map_flatten.fixed +++ b/tests/ui/map_flatten.fixed @@ -5,6 +5,7 @@ #![allow(clippy::missing_docs_in_private_items)] #![allow(clippy::map_identity)] #![allow(clippy::unnecessary_wraps)] +#![feature(result_flattening)] fn main() { // mapping to Option on Iterator @@ -23,4 +24,7 @@ fn main() { // mapping to Option on Option let _: Option<_> = (Some(Some(1))).and_then(|x| x); + + // mapping to Result on Result + let _: Result<_, &str> = (Ok(Ok(1))).and_then(|x| x); } diff --git a/tests/ui/map_flatten.rs b/tests/ui/map_flatten.rs index 578bd87726795..01db27876da70 100644 --- a/tests/ui/map_flatten.rs +++ b/tests/ui/map_flatten.rs @@ -5,6 +5,7 @@ #![allow(clippy::missing_docs_in_private_items)] #![allow(clippy::map_identity)] #![allow(clippy::unnecessary_wraps)] +#![feature(result_flattening)] fn main() { // mapping to Option on Iterator @@ -23,4 +24,7 @@ fn main() { // mapping to Option on Option let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); + + // mapping to Result on Result + let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten(); } diff --git a/tests/ui/map_flatten.stderr b/tests/ui/map_flatten.stderr index 756e6e818ad44..38457c8ea4dd4 100644 --- a/tests/ui/map_flatten.stderr +++ b/tests/ui/map_flatten.stderr @@ -1,5 +1,5 @@ error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:16:46 + --> $DIR/map_flatten.rs:17:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id)` @@ -7,34 +7,40 @@ LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().coll = note: `-D clippy::map-flatten` implied by `-D warnings` error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:17:46 + --> $DIR/map_flatten.rs:18:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_ref)` error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:18:46 + --> $DIR/map_flatten.rs:19:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_closure)` error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:19:46 + --> $DIR/map_flatten.rs:20:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(|x| x.checked_add(1))` error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:22:46 + --> $DIR/map_flatten.rs:23:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `.flat_map(|x| 0..x)` error: called `map(..).flatten()` on an `Option` - --> $DIR/map_flatten.rs:25:39 + --> $DIR/map_flatten.rs:26:39 | LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); | ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)` -error: aborting due to 6 previous errors +error: called `map(..).flatten()` on an `Result` + --> $DIR/map_flatten.rs:29:41 + | +LL | let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten(); + | ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)` + +error: aborting due to 7 previous errors diff --git a/tests/ui/never_loop.stderr b/tests/ui/never_loop.stderr index c00b4c78cf28b..f49b23924efe8 100644 --- a/tests/ui/never_loop.stderr +++ b/tests/ui/never_loop.stderr @@ -75,6 +75,11 @@ LL | | _ => return, LL | | } LL | | } | |_____^ + | +help: if you need the first element of the iterator, try writing + | +LL | if let Some(x) = (0..10).next() { + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: this loop never actually loops --> $DIR/never_loop.rs:157:5 diff --git a/tests/ui/no_effect.stderr b/tests/ui/no_effect.stderr index 834b9056e311d..6b24675ac2d42 100644 --- a/tests/ui/no_effect.stderr +++ b/tests/ui/no_effect.stderr @@ -108,6 +108,12 @@ error: statement with no effect LL | 5..6; | ^^^^^ +error: statement with no effect + --> $DIR/no_effect.rs:83:5 + | +LL | 5..=6; + | ^^^^^^ + error: statement with no effect --> $DIR/no_effect.rs:84:5 | @@ -150,5 +156,5 @@ error: statement with no effect LL | FooString { s: s }; | ^^^^^^^^^^^^^^^^^^^ -error: aborting due to 25 previous errors +error: aborting due to 26 previous errors diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 4390ff7dc3044..c2f94d0e85756 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -18,6 +18,19 @@ fn or_fun_call() { } } + struct FakeDefault; + impl FakeDefault { + fn default() -> Self { + FakeDefault + } + } + + impl Default for FakeDefault { + fn default() -> Self { + FakeDefault + } + } + enum Enum { A(i32), } @@ -53,6 +66,12 @@ fn or_fun_call() { let with_default_type = Some(1); with_default_type.unwrap_or_default(); + let self_default = None::; + self_default.unwrap_or_else(::default); + + let real_default = None::; + real_default.unwrap_or_default(); + let with_vec = Some(vec![1]); with_vec.unwrap_or_default(); diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 75908c974cc9d..afaf92961b027 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -18,6 +18,19 @@ fn or_fun_call() { } } + struct FakeDefault; + impl FakeDefault { + fn default() -> Self { + FakeDefault + } + } + + impl Default for FakeDefault { + fn default() -> Self { + FakeDefault + } + } + enum Enum { A(i32), } @@ -53,6 +66,12 @@ fn or_fun_call() { let with_default_type = Some(1); with_default_type.unwrap_or(u64::default()); + let self_default = None::; + self_default.unwrap_or(::default()); + + let real_default = None::; + real_default.unwrap_or(::default()); + let with_vec = Some(vec![1]); with_vec.unwrap_or(vec![]); diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 9905029ce91fa..b2bcbd38c2df3 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -1,5 +1,5 @@ error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:33:19 + --> $DIR/or_fun_call.rs:46:19 | LL | with_const_fn.unwrap_or(Duration::from_secs(5)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| Duration::from_secs(5))` @@ -7,130 +7,142 @@ LL | with_const_fn.unwrap_or(Duration::from_secs(5)); = note: `-D clippy::or-fun-call` implied by `-D warnings` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:36:22 + --> $DIR/or_fun_call.rs:49:22 | LL | with_constructor.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(make)` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:39:5 + --> $DIR/or_fun_call.rs:52:5 | LL | with_new.unwrap_or(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_new.unwrap_or_default()` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:42:21 + --> $DIR/or_fun_call.rs:55:21 | LL | with_const_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| Vec::with_capacity(12))` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:45:14 + --> $DIR/or_fun_call.rs:58:14 | LL | with_err.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| make())` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:48:19 + --> $DIR/or_fun_call.rs:61:19 | LL | with_err_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| Vec::with_capacity(12))` error: use of `unwrap_or` followed by a call to `default` - --> $DIR/or_fun_call.rs:51:5 + --> $DIR/or_fun_call.rs:64:5 | LL | with_default_trait.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_default_trait.unwrap_or_default()` error: use of `unwrap_or` followed by a call to `default` - --> $DIR/or_fun_call.rs:54:5 + --> $DIR/or_fun_call.rs:67:5 | LL | with_default_type.unwrap_or(u64::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_default_type.unwrap_or_default()` +error: use of `unwrap_or` followed by a function call + --> $DIR/or_fun_call.rs:70:18 + | +LL | self_default.unwrap_or(::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(::default)` + +error: use of `unwrap_or` followed by a call to `default` + --> $DIR/or_fun_call.rs:73:5 + | +LL | real_default.unwrap_or(::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `real_default.unwrap_or_default()` + error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:57:5 + --> $DIR/or_fun_call.rs:76:5 | LL | with_vec.unwrap_or(vec![]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_vec.unwrap_or_default()` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:60:21 + --> $DIR/or_fun_call.rs:79:21 | LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)` error: use of `or_insert` followed by a function call - --> $DIR/or_fun_call.rs:63:19 + --> $DIR/or_fun_call.rs:82:19 | LL | map.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` error: use of `or_insert` followed by a function call - --> $DIR/or_fun_call.rs:66:23 + --> $DIR/or_fun_call.rs:85:23 | LL | map_vec.entry(42).or_insert(vec![]); | ^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(Vec::new)` error: use of `or_insert` followed by a function call - --> $DIR/or_fun_call.rs:69:21 + --> $DIR/or_fun_call.rs:88:21 | LL | btree.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` error: use of `or_insert` followed by a function call - --> $DIR/or_fun_call.rs:72:25 + --> $DIR/or_fun_call.rs:91:25 | LL | btree_vec.entry(42).or_insert(vec![]); | ^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(Vec::new)` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:75:21 + --> $DIR/or_fun_call.rs:94:21 | LL | let _ = stringy.unwrap_or("".to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:83:21 + --> $DIR/or_fun_call.rs:102:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:85:21 + --> $DIR/or_fun_call.rs:104:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:109:35 + --> $DIR/or_fun_call.rs:128:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:113:10 + --> $DIR/or_fun_call.rs:132:10 | LL | .or(Some(Bar(b, Duration::from_secs(2)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(Bar(b, Duration::from_secs(2))))` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:141:14 + --> $DIR/or_fun_call.rs:160:14 | LL | None.unwrap_or(s.as_mut_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| s.as_mut_vec())` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:146:14 + --> $DIR/or_fun_call.rs:165:14 | LL | None.unwrap_or(unsafe { s.as_mut_vec() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { s.as_mut_vec() })` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:148:14 + --> $DIR/or_fun_call.rs:167:14 | LL | None.unwrap_or( unsafe { s.as_mut_vec() } ); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { s.as_mut_vec() })` -error: aborting due to 22 previous errors +error: aborting due to 24 previous errors diff --git a/tests/ui/similar_names.rs b/tests/ui/similar_names.rs index 2b1bc1f485959..daa073414577c 100644 --- a/tests/ui/similar_names.rs +++ b/tests/ui/similar_names.rs @@ -76,6 +76,9 @@ fn main() { // names often used in win32 code (for example WindowProc) let wparam: i32; let lparam: i32; + + let iter: i32; + let item: i32; } fn foo() { diff --git a/tests/ui/similar_names.stderr b/tests/ui/similar_names.stderr index b24accd962a7f..f621595abaea2 100644 --- a/tests/ui/similar_names.stderr +++ b/tests/ui/similar_names.stderr @@ -72,13 +72,13 @@ LL | let parser: i32; | ^^^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:85:16 + --> $DIR/similar_names.rs:88:16 | LL | bpple: sprang, | ^^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:84:16 + --> $DIR/similar_names.rs:87:16 | LL | apple: spring, | ^^^^^^ diff --git a/tests/ui/swap.fixed b/tests/ui/swap.fixed index 0f8f839a0d542..ef518359ec5f3 100644 --- a/tests/ui/swap.fixed +++ b/tests/ui/swap.fixed @@ -6,6 +6,7 @@ clippy::no_effect, clippy::redundant_clone, redundant_semicolons, + dead_code, unused_assignments )] @@ -20,9 +21,7 @@ struct Bar { fn field() { let mut bar = Bar { a: 1, b: 2 }; - let temp = bar.a; - bar.a = bar.b; - bar.b = temp; + std::mem::swap(&mut bar.a, &mut bar.b); let mut baz = vec![bar.clone(), bar.clone()]; let temp = baz[0].a; @@ -51,6 +50,7 @@ fn unswappable_slice() { foo[1][0] = temp; // swap(foo[0][1], foo[1][0]) would fail + // this could use split_at_mut and mem::swap, but that is not much simpler. } fn vec() { @@ -60,13 +60,54 @@ fn vec() { foo.swap(0, 1); } +fn xor_swap_locals() { + // This is an xor-based swap of local variables. + let mut a = 0; + let mut b = 1; + std::mem::swap(&mut a, &mut b) +} + +fn xor_field_swap() { + // This is an xor-based swap of fields in a struct. + let mut bar = Bar { a: 0, b: 1 }; + std::mem::swap(&mut bar.a, &mut bar.b) +} + +fn xor_slice_swap() { + // This is an xor-based swap of a slice + let foo = &mut [1, 2]; + foo.swap(0, 1) +} + +fn xor_no_swap() { + // This is a sequence of xor-assignment statements that doesn't result in a swap. + let mut a = 0; + let mut b = 1; + let mut c = 2; + a ^= b; + b ^= c; + a ^= c; + c ^= a; +} + +fn xor_unswappable_slice() { + let foo = &mut [vec![1, 2], vec![3, 4]]; + foo[0][1] ^= foo[1][0]; + foo[1][0] ^= foo[0][0]; + foo[0][1] ^= foo[1][0]; + + // swap(foo[0][1], foo[1][0]) would fail + // this could use split_at_mut and mem::swap, but that is not much simpler. +} + +fn distinct_slice() { + let foo = &mut [vec![1, 2], vec![3, 4]]; + let bar = &mut [vec![1, 2], vec![3, 4]]; + std::mem::swap(&mut foo[0][1], &mut bar[1][0]); +} + #[rustfmt::skip] fn main() { - field(); - array(); - slice(); - unswappable_slice(); - vec(); let mut a = 42; let mut b = 1337; diff --git a/tests/ui/swap.rs b/tests/ui/swap.rs index 5763d9e82d486..8518659ccf316 100644 --- a/tests/ui/swap.rs +++ b/tests/ui/swap.rs @@ -6,6 +6,7 @@ clippy::no_effect, clippy::redundant_clone, redundant_semicolons, + dead_code, unused_assignments )] @@ -55,6 +56,7 @@ fn unswappable_slice() { foo[1][0] = temp; // swap(foo[0][1], foo[1][0]) would fail + // this could use split_at_mut and mem::swap, but that is not much simpler. } fn vec() { @@ -66,13 +68,62 @@ fn vec() { foo.swap(0, 1); } +fn xor_swap_locals() { + // This is an xor-based swap of local variables. + let mut a = 0; + let mut b = 1; + a ^= b; + b ^= a; + a ^= b; +} + +fn xor_field_swap() { + // This is an xor-based swap of fields in a struct. + let mut bar = Bar { a: 0, b: 1 }; + bar.a ^= bar.b; + bar.b ^= bar.a; + bar.a ^= bar.b; +} + +fn xor_slice_swap() { + // This is an xor-based swap of a slice + let foo = &mut [1, 2]; + foo[0] ^= foo[1]; + foo[1] ^= foo[0]; + foo[0] ^= foo[1]; +} + +fn xor_no_swap() { + // This is a sequence of xor-assignment statements that doesn't result in a swap. + let mut a = 0; + let mut b = 1; + let mut c = 2; + a ^= b; + b ^= c; + a ^= c; + c ^= a; +} + +fn xor_unswappable_slice() { + let foo = &mut [vec![1, 2], vec![3, 4]]; + foo[0][1] ^= foo[1][0]; + foo[1][0] ^= foo[0][0]; + foo[0][1] ^= foo[1][0]; + + // swap(foo[0][1], foo[1][0]) would fail + // this could use split_at_mut and mem::swap, but that is not much simpler. +} + +fn distinct_slice() { + let foo = &mut [vec![1, 2], vec![3, 4]]; + let bar = &mut [vec![1, 2], vec![3, 4]]; + let temp = foo[0][1]; + foo[0][1] = bar[1][0]; + bar[1][0] = temp; +} + #[rustfmt::skip] fn main() { - field(); - array(); - slice(); - unswappable_slice(); - vec(); let mut a = 42; let mut b = 1337; diff --git a/tests/ui/swap.stderr b/tests/ui/swap.stderr index f49bcfedf3a19..614d16ced40f1 100644 --- a/tests/ui/swap.stderr +++ b/tests/ui/swap.stderr @@ -1,15 +1,24 @@ +error: this looks like you are swapping `bar.a` and `bar.b` manually + --> $DIR/swap.rs:24:5 + | +LL | / let temp = bar.a; +LL | | bar.a = bar.b; +LL | | bar.b = temp; + | |________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b)` + | + = note: `-D clippy::manual-swap` implied by `-D warnings` + = note: or maybe you should use `std::mem::replace`? + error: this looks like you are swapping elements of `foo` manually - --> $DIR/swap.rs:35:5 + --> $DIR/swap.rs:36:5 | LL | / let temp = foo[0]; LL | | foo[0] = foo[1]; LL | | foo[1] = temp; | |_________________^ help: try: `foo.swap(0, 1)` - | - = note: `-D clippy::manual-swap` implied by `-D warnings` error: this looks like you are swapping elements of `foo` manually - --> $DIR/swap.rs:44:5 + --> $DIR/swap.rs:45:5 | LL | / let temp = foo[0]; LL | | foo[0] = foo[1]; @@ -17,7 +26,7 @@ LL | | foo[1] = temp; | |_________________^ help: try: `foo.swap(0, 1)` error: this looks like you are swapping elements of `foo` manually - --> $DIR/swap.rs:62:5 + --> $DIR/swap.rs:64:5 | LL | / let temp = foo[0]; LL | | foo[0] = foo[1]; @@ -25,7 +34,41 @@ LL | | foo[1] = temp; | |_________________^ help: try: `foo.swap(0, 1)` error: this looks like you are swapping `a` and `b` manually - --> $DIR/swap.rs:83:7 + --> $DIR/swap.rs:75:5 + | +LL | / a ^= b; +LL | | b ^= a; +LL | | a ^= b; + | |___________^ help: try: `std::mem::swap(&mut a, &mut b)` + +error: this looks like you are swapping `bar.a` and `bar.b` manually + --> $DIR/swap.rs:83:5 + | +LL | / bar.a ^= bar.b; +LL | | bar.b ^= bar.a; +LL | | bar.a ^= bar.b; + | |___________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b)` + +error: this looks like you are swapping elements of `foo` manually + --> $DIR/swap.rs:91:5 + | +LL | / foo[0] ^= foo[1]; +LL | | foo[1] ^= foo[0]; +LL | | foo[0] ^= foo[1]; + | |_____________________^ help: try: `foo.swap(0, 1)` + +error: this looks like you are swapping `foo[0][1]` and `bar[1][0]` manually + --> $DIR/swap.rs:120:5 + | +LL | / let temp = foo[0][1]; +LL | | foo[0][1] = bar[1][0]; +LL | | bar[1][0] = temp; + | |____________________^ help: try: `std::mem::swap(&mut foo[0][1], &mut bar[1][0])` + | + = note: or maybe you should use `std::mem::replace`? + +error: this looks like you are swapping `a` and `b` manually + --> $DIR/swap.rs:134:7 | LL | ; let t = a; | _______^ @@ -36,7 +79,7 @@ LL | | b = t; = note: or maybe you should use `std::mem::replace`? error: this looks like you are swapping `c.0` and `a` manually - --> $DIR/swap.rs:92:7 + --> $DIR/swap.rs:143:7 | LL | ; let t = c.0; | _______^ @@ -47,7 +90,7 @@ LL | | a = t; = note: or maybe you should use `std::mem::replace`? error: this looks like you are trying to swap `a` and `b` - --> $DIR/swap.rs:80:5 + --> $DIR/swap.rs:131:5 | LL | / a = b; LL | | b = a; @@ -57,7 +100,7 @@ LL | | b = a; = note: or maybe you should use `std::mem::replace`? error: this looks like you are trying to swap `c.0` and `a` - --> $DIR/swap.rs:89:5 + --> $DIR/swap.rs:140:5 | LL | / c.0 = a; LL | | a = c.0; @@ -65,5 +108,5 @@ LL | | a = c.0; | = note: or maybe you should use `std::mem::replace`? -error: aborting due to 7 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/trivially_copy_pass_by_ref.rs b/tests/ui/trivially_copy_pass_by_ref.rs index e7e0a31febc45..1a0123803a3ee 100644 --- a/tests/ui/trivially_copy_pass_by_ref.rs +++ b/tests/ui/trivially_copy_pass_by_ref.rs @@ -58,6 +58,8 @@ impl Foo { fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} fn bad2(x: &u32, y: &Foo, z: &Baz) {} + + fn bad_issue7518(self, other: &Self) {} } impl AsRef for Foo { diff --git a/tests/ui/trivially_copy_pass_by_ref.stderr b/tests/ui/trivially_copy_pass_by_ref.stderr index 2b0005bbff1db..9c4c49ceac476 100644 --- a/tests/ui/trivially_copy_pass_by_ref.stderr +++ b/tests/ui/trivially_copy_pass_by_ref.stderr @@ -65,40 +65,46 @@ LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:72:16 + --> $DIR/trivially_copy_pass_by_ref.rs:62:35 + | +LL | fn bad_issue7518(self, other: &Self) {} + | ^^^^^ help: consider passing by value instead: `Self` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:74:16 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:72:25 + --> $DIR/trivially_copy_pass_by_ref.rs:74:25 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:72:34 + --> $DIR/trivially_copy_pass_by_ref.rs:74:34 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:76:34 + --> $DIR/trivially_copy_pass_by_ref.rs:78:34 | LL | fn trait_method(&self, _foo: &Foo); | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:108:21 + --> $DIR/trivially_copy_pass_by_ref.rs:110:21 | LL | fn foo_never(x: &i32) { | ^^^^ help: consider passing by value instead: `i32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:113:15 + --> $DIR/trivially_copy_pass_by_ref.rs:115:15 | LL | fn foo(x: &i32) { | ^^^^ help: consider passing by value instead: `i32` -error: aborting due to 16 previous errors +error: aborting due to 17 previous errors diff --git a/tests/ui/unwrap_or_else_default.fixed b/tests/ui/unwrap_or_else_default.fixed new file mode 100644 index 0000000000000..7ac3f426c9775 --- /dev/null +++ b/tests/ui/unwrap_or_else_default.fixed @@ -0,0 +1,71 @@ +// run-rustfix + +#![warn(clippy::unwrap_or_else_default)] +#![allow(dead_code)] +#![allow(clippy::unnecessary_wraps)] + +/// Checks implementation of the `UNWRAP_OR_ELSE_DEFAULT` lint. +fn unwrap_or_else_default() { + struct Foo; + + impl Foo { + fn new() -> Foo { + Foo + } + + // fake default, we should not trigger on this + fn default() -> Foo { + Foo + } + } + + struct HasDefaultAndDuplicate; + + impl HasDefaultAndDuplicate { + fn default() -> Self { + HasDefaultAndDuplicate + } + } + + impl Default for HasDefaultAndDuplicate { + fn default() -> Self { + HasDefaultAndDuplicate + } + } + + enum Enum { + A(), + } + + fn make(_: V) -> T { + unimplemented!(); + } + + let with_enum = Some(Enum::A()); + with_enum.unwrap_or_else(Enum::A); + + let with_new = Some(vec![1]); + with_new.unwrap_or_else(Vec::new); + + let with_err: Result<_, ()> = Ok(vec![1]); + with_err.unwrap_or_else(make); + + // should not be changed + let with_fake_default = None::; + with_fake_default.unwrap_or_else(Foo::default); + + // should not be changed + let with_fake_default2 = None::; + with_fake_default2.unwrap_or_else(::default); + + let with_real_default = None::; + with_real_default.unwrap_or_default(); + + let with_default_trait = Some(1); + with_default_trait.unwrap_or_default(); + + let with_default_type = Some(1); + with_default_type.unwrap_or_default(); +} + +fn main() {} diff --git a/tests/ui/unwrap_or_else_default.rs b/tests/ui/unwrap_or_else_default.rs new file mode 100644 index 0000000000000..82b727a039ed4 --- /dev/null +++ b/tests/ui/unwrap_or_else_default.rs @@ -0,0 +1,71 @@ +// run-rustfix + +#![warn(clippy::unwrap_or_else_default)] +#![allow(dead_code)] +#![allow(clippy::unnecessary_wraps)] + +/// Checks implementation of the `UNWRAP_OR_ELSE_DEFAULT` lint. +fn unwrap_or_else_default() { + struct Foo; + + impl Foo { + fn new() -> Foo { + Foo + } + + // fake default, we should not trigger on this + fn default() -> Foo { + Foo + } + } + + struct HasDefaultAndDuplicate; + + impl HasDefaultAndDuplicate { + fn default() -> Self { + HasDefaultAndDuplicate + } + } + + impl Default for HasDefaultAndDuplicate { + fn default() -> Self { + HasDefaultAndDuplicate + } + } + + enum Enum { + A(), + } + + fn make(_: V) -> T { + unimplemented!(); + } + + let with_enum = Some(Enum::A()); + with_enum.unwrap_or_else(Enum::A); + + let with_new = Some(vec![1]); + with_new.unwrap_or_else(Vec::new); + + let with_err: Result<_, ()> = Ok(vec![1]); + with_err.unwrap_or_else(make); + + // should not be changed + let with_fake_default = None::; + with_fake_default.unwrap_or_else(Foo::default); + + // should not be changed + let with_fake_default2 = None::; + with_fake_default2.unwrap_or_else(::default); + + let with_real_default = None::; + with_real_default.unwrap_or_else(::default); + + let with_default_trait = Some(1); + with_default_trait.unwrap_or_else(Default::default); + + let with_default_type = Some(1); + with_default_type.unwrap_or_else(u64::default); +} + +fn main() {} diff --git a/tests/ui/unwrap_or_else_default.stderr b/tests/ui/unwrap_or_else_default.stderr new file mode 100644 index 0000000000000..feb215b09f662 --- /dev/null +++ b/tests/ui/unwrap_or_else_default.stderr @@ -0,0 +1,22 @@ +error: use of `.unwrap_or_else(..)` to construct default value + --> $DIR/unwrap_or_else_default.rs:62:5 + | +LL | with_real_default.unwrap_or_else(::default); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `with_real_default.unwrap_or_default()` + | + = note: `-D clippy::unwrap-or-else-default` implied by `-D warnings` + +error: use of `.unwrap_or_else(..)` to construct default value + --> $DIR/unwrap_or_else_default.rs:65:5 + | +LL | with_default_trait.unwrap_or_else(Default::default); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `with_default_trait.unwrap_or_default()` + +error: use of `.unwrap_or_else(..)` to construct default value + --> $DIR/unwrap_or_else_default.rs:68:5 + | +LL | with_default_type.unwrap_or_else(u64::default); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `with_default_type.unwrap_or_default()` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/while_let_on_iterator.fixed b/tests/ui/while_let_on_iterator.fixed index 52e80ceee83cf..cdcdd808c9444 100644 --- a/tests/ui/while_let_on_iterator.fixed +++ b/tests/ui/while_let_on_iterator.fixed @@ -334,6 +334,38 @@ fn issue7249() { x(); } +fn issue7510() { + let mut it = 0..10; + let it = &mut it; + // Needs to reborrow `it` as the binding isn't mutable + for x in &mut *it { + if x % 2 == 0 { + break; + } + } + println!("{}", it.next().unwrap()); + + struct S(T); + let mut it = 0..10; + let it = S(&mut it); + // Needs to reborrow `it.0` as the binding isn't mutable + for x in &mut *it.0 { + if x % 2 == 0 { + break; + } + } + println!("{}", it.0.next().unwrap()); +} + +fn exact_match_with_single_field() { + struct S(T); + let mut s = S(0..10); + // Don't lint. `s.0` is used inside the loop. + while let Some(_) = s.0.next() { + let _ = &mut s.0; + } +} + fn main() { let mut it = 0..20; for _ in it { diff --git a/tests/ui/while_let_on_iterator.rs b/tests/ui/while_let_on_iterator.rs index 5078a3c9028c4..72f34257d1f46 100644 --- a/tests/ui/while_let_on_iterator.rs +++ b/tests/ui/while_let_on_iterator.rs @@ -334,6 +334,38 @@ fn issue7249() { x(); } +fn issue7510() { + let mut it = 0..10; + let it = &mut it; + // Needs to reborrow `it` as the binding isn't mutable + while let Some(x) = it.next() { + if x % 2 == 0 { + break; + } + } + println!("{}", it.next().unwrap()); + + struct S(T); + let mut it = 0..10; + let it = S(&mut it); + // Needs to reborrow `it.0` as the binding isn't mutable + while let Some(x) = it.0.next() { + if x % 2 == 0 { + break; + } + } + println!("{}", it.0.next().unwrap()); +} + +fn exact_match_with_single_field() { + struct S(T); + let mut s = S(0..10); + // Don't lint. `s.0` is used inside the loop. + while let Some(_) = s.0.next() { + let _ = &mut s.0; + } +} + fn main() { let mut it = 0..20; while let Some(..) = it.next() { diff --git a/tests/ui/while_let_on_iterator.stderr b/tests/ui/while_let_on_iterator.stderr index cb0afeae15ee0..ff9b08996da53 100644 --- a/tests/ui/while_let_on_iterator.stderr +++ b/tests/ui/while_let_on_iterator.stderr @@ -111,10 +111,22 @@ LL | while let Some(x) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in &mut it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:339:5 + --> $DIR/while_let_on_iterator.rs:341:5 + | +LL | while let Some(x) = it.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in &mut *it` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:352:5 + | +LL | while let Some(x) = it.0.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in &mut *it.0` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:371:5 | LL | while let Some(..) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it` -error: aborting due to 19 previous errors +error: aborting due to 21 previous errors From 09b7745f34b8d81c1d4517975f2e552274977f45 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Thu, 12 Aug 2021 12:11:26 +0200 Subject: [PATCH 04/94] Updated lint message for `rc_mutex` --- clippy_lints/src/types/rc_mutex.rs | 11 ++++++----- tests/ui/rc_mutex.stderr | 15 +++++++++++---- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/types/rc_mutex.rs b/clippy_lints/src/types/rc_mutex.rs index bd7a0ee6408fa..12db7afb81ca1 100644 --- a/clippy_lints/src/types/rc_mutex.rs +++ b/clippy_lints/src/types/rc_mutex.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::is_ty_param_diagnostic_item; use if_chain::if_chain; use rustc_hir::{self as hir, def_id::DefId, QPath}; @@ -11,13 +11,14 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ if_chain! { if cx.tcx.is_diagnostic_item(sym::Rc, def_id) ; if let Some(_) = is_ty_param_diagnostic_item(cx, qpath, sym!(mutex_type)) ; - - then{ - span_lint( + then { + span_lint_and_help( cx, RC_MUTEX, hir_ty.span, - "found `Rc>`. Consider using `Rc>` or `Arc>` instead", + "usage of `Rc>`", + None, + "consider using `Rc>` or `Arc>` instead", ); return true; } diff --git a/tests/ui/rc_mutex.stderr b/tests/ui/rc_mutex.stderr index 8e58e2bc2d0bf..852e415cf1e2e 100644 --- a/tests/ui/rc_mutex.stderr +++ b/tests/ui/rc_mutex.stderr @@ -1,28 +1,35 @@ -error: found `Rc>`. Consider using `Rc>` or `Arc>` instead +error: usage of `Rc>` --> $DIR/rc_mutex.rs:8:10 | LL | foo: Rc>, | ^^^^^^^^^^^^^^ | = note: `-D clippy::rc-mutex` implied by `-D warnings` + = help: consider using `Rc>` or `Arc>` instead -error: found `Rc>`. Consider using `Rc>` or `Arc>` instead +error: usage of `Rc>` --> $DIR/rc_mutex.rs:20:22 | LL | pub fn test1(foo: Rc>) {} | ^^^^^^^^^^^^ + | + = help: consider using `Rc>` or `Arc>` instead -error: found `Rc>`. Consider using `Rc>` or `Arc>` instead +error: usage of `Rc>` --> $DIR/rc_mutex.rs:22:19 | LL | pub fn test2(foo: Rc>) {} | ^^^^^^^^^^^^^^^^^ + | + = help: consider using `Rc>` or `Arc>` instead -error: found `Rc>`. Consider using `Rc>` or `Arc>` instead +error: usage of `Rc>` --> $DIR/rc_mutex.rs:24:19 | LL | pub fn test3(foo: Rc>>) {} | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using `Rc>` or `Arc>` instead error: aborting due to 4 previous errors From 206741bf57361efcd04243b9e2e6275701c15c76 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Thu, 12 Aug 2021 12:05:02 +0200 Subject: [PATCH 05/94] Use `avoid_breaking_exported_api` for `types` module lints Addressed PR reviews regarding code style --- clippy_lints/src/lib.rs | 7 ++- clippy_lints/src/types/mod.rs | 81 +++++++++++++++++++++++++++------- clippy_lints/src/utils/conf.rs | 2 +- 3 files changed, 71 insertions(+), 19 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index dbdb4251b3bec..18600498e1c46 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1840,7 +1840,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box serde_api::SerdeApi); let vec_box_size_threshold = conf.vec_box_size_threshold; let type_complexity_threshold = conf.type_complexity_threshold; - store.register_late_pass(move || box types::Types::new(vec_box_size_threshold, type_complexity_threshold)); + let avoid_breaking_exported_api = conf.avoid_breaking_exported_api; + store.register_late_pass(move || box types::Types::new( + vec_box_size_threshold, + type_complexity_threshold, + avoid_breaking_exported_api, + )); store.register_late_pass(|| box booleans::NonminimalBool); store.register_late_pass(|| box needless_bitwise_bool::NeedlessBitwiseBool); store.register_late_pass(|| box eq_op::EqOp); diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 371bb8b445a71..9588de8459cfe 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -295,6 +295,7 @@ declare_clippy_lint! { pub struct Types { vec_box_size_threshold: u64, type_complexity_threshold: u64, + avoid_breaking_exported_api: bool, } impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER, RC_MUTEX, TYPE_COMPLEXITY]); @@ -308,19 +309,31 @@ impl<'tcx> LateLintPass<'tcx> for Types { false }; + let is_exported = cx.access_levels.is_exported(cx.tcx.hir().local_def_id(id)); + self.check_fn_decl( cx, decl, CheckTyContext { is_in_trait_impl, + is_exported, ..CheckTyContext::default() }, ); } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + let is_exported = cx.access_levels.is_exported(item.def_id); + match item.kind { - ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _) => self.check_ty(cx, ty, CheckTyContext::default()), + ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _) => self.check_ty( + cx, + ty, + CheckTyContext { + is_exported, + ..CheckTyContext::default() + }, + ), // functions, enums, structs, impls and traits are covered _ => (), } @@ -342,15 +355,31 @@ impl<'tcx> LateLintPass<'tcx> for Types { } fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) { - self.check_ty(cx, field.ty, CheckTyContext::default()); + let is_exported = cx.access_levels.is_exported(cx.tcx.hir().local_def_id(field.hir_id)); + + self.check_ty( + cx, + field.ty, + CheckTyContext { + is_exported, + ..CheckTyContext::default() + }, + ); } - fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) { + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &TraitItem<'_>) { + let is_exported = cx.access_levels.is_exported(item.def_id); + + let context = CheckTyContext { + is_exported, + ..CheckTyContext::default() + }; + match item.kind { TraitItemKind::Const(ty, _) | TraitItemKind::Type(_, Some(ty)) => { - self.check_ty(cx, ty, CheckTyContext::default()); + self.check_ty(cx, ty, context); }, - TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, sig.decl, CheckTyContext::default()), + TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, sig.decl, context), TraitItemKind::Type(..) => (), } } @@ -370,10 +399,11 @@ impl<'tcx> LateLintPass<'tcx> for Types { } impl Types { - pub fn new(vec_box_size_threshold: u64, type_complexity_threshold: u64) -> Self { + pub fn new(vec_box_size_threshold: u64, type_complexity_threshold: u64, avoid_breaking_exported_api: bool) -> Self { Self { vec_box_size_threshold, type_complexity_threshold, + avoid_breaking_exported_api, } } @@ -410,17 +440,24 @@ impl Types { let hir_id = hir_ty.hir_id; let res = cx.qpath_res(qpath, hir_id); if let Some(def_id) = res.opt_def_id() { - let mut triggered = false; - triggered |= box_vec::check(cx, hir_ty, qpath, def_id); - triggered |= redundant_allocation::check(cx, hir_ty, qpath, def_id); - triggered |= rc_buffer::check(cx, hir_ty, qpath, def_id); - triggered |= vec_box::check(cx, hir_ty, qpath, def_id, self.vec_box_size_threshold); - triggered |= option_option::check(cx, hir_ty, qpath, def_id); - triggered |= linked_list::check(cx, hir_ty, def_id); - triggered |= rc_mutex::check(cx, hir_ty, qpath, def_id); - - if triggered { - return; + if self.is_type_change_allowed(context) { + // All lints that are being checked in this block are guarded by + // the `avoid_breaking_exported_api` configuration. When adding a + // new lint, please also add the name to the configuration documentation + // in `clippy_lints::utils::conf.rs` + + let mut triggered = false; + triggered |= box_vec::check(cx, hir_ty, qpath, def_id); + triggered |= redundant_allocation::check(cx, hir_ty, qpath, def_id); + triggered |= rc_buffer::check(cx, hir_ty, qpath, def_id); + triggered |= vec_box::check(cx, hir_ty, qpath, def_id, self.vec_box_size_threshold); + triggered |= option_option::check(cx, hir_ty, qpath, def_id); + triggered |= linked_list::check(cx, hir_ty, def_id); + triggered |= rc_mutex::check(cx, hir_ty, qpath, def_id); + + if triggered { + return; + } } } match *qpath { @@ -487,11 +524,21 @@ impl Types { _ => {}, } } + + /// This function checks if the type is allowed to change in the current context + /// based on the `avoid_breaking_exported_api` configuration + fn is_type_change_allowed(&self, context: CheckTyContext) -> bool { + !(context.is_exported && self.avoid_breaking_exported_api) + } } +#[allow(clippy::struct_excessive_bools)] #[derive(Clone, Copy, Default)] struct CheckTyContext { is_in_trait_impl: bool, + /// `true` for types on local variables. is_local: bool, + /// `true` for types that are part of the public API. + is_exported: bool, is_nested_call: bool, } diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index a28b1d78f7d46..9ee2e3024520b 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -132,7 +132,7 @@ macro_rules! define_Conf { // N.B., this macro is parsed by util/lintlib.py define_Conf! { - /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION. + /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_VEC, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX. /// /// Suppress lints whenever the suggested change would cause breakage for other crates. (avoid_breaking_exported_api: bool = true), From c02dcd5405cc11270bef963c37837a944a672f6a Mon Sep 17 00:00:00 2001 From: xFrednet Date: Thu, 12 Aug 2021 13:15:15 +0200 Subject: [PATCH 06/94] Update type UI tests to use private items --- tests/ui/box_vec.rs | 28 ++++++++++++++++------------ tests/ui/box_vec.stderr | 6 +++--- tests/ui/linkedlist.rs | 31 ++++++++++++++++++------------- tests/ui/linkedlist.stderr | 24 ++++++++++++++++-------- tests/ui/rc_mutex.rs | 32 +++++++++++++++++--------------- tests/ui/rc_mutex.stderr | 18 +++++++++--------- 6 files changed, 79 insertions(+), 60 deletions(-) diff --git a/tests/ui/box_vec.rs b/tests/ui/box_vec.rs index 87b67c23704c9..1d6366972dacf 100644 --- a/tests/ui/box_vec.rs +++ b/tests/ui/box_vec.rs @@ -1,6 +1,10 @@ #![warn(clippy::all)] -#![allow(clippy::boxed_local, clippy::needless_pass_by_value)] -#![allow(clippy::blacklisted_name)] +#![allow( + clippy::boxed_local, + clippy::needless_pass_by_value, + clippy::blacklisted_name, + unused +)] macro_rules! boxit { ($init:expr, $x:ty) => { @@ -11,22 +15,22 @@ macro_rules! boxit { fn test_macro() { boxit!(Vec::new(), Vec); } -pub fn test(foo: Box>) { - println!("{:?}", foo.get(0)) -} +fn test(foo: Box>) {} -pub fn test2(foo: Box)>) { +fn test2(foo: Box)>) { // pass if #31 is fixed foo(vec![1, 2, 3]) } -pub fn test_local_not_linted() { +fn test_local_not_linted() { let _: Box>; } -fn main() { - test(Box::new(Vec::new())); - test2(Box::new(|v| println!("{:?}", v))); - test_macro(); - test_local_not_linted(); +// All of these test should be allowed because they are part of the +// public api and `avoid_breaking_exported_api` is `false` by default. +pub fn pub_test(foo: Box>) {} +pub fn pub_test_ret() -> Box> { + Box::new(Vec::new()) } + +fn main() {} diff --git a/tests/ui/box_vec.stderr b/tests/ui/box_vec.stderr index 9b789334baeec..58c1f13fb877b 100644 --- a/tests/ui/box_vec.stderr +++ b/tests/ui/box_vec.stderr @@ -1,8 +1,8 @@ error: you seem to be trying to use `Box>`. Consider using just `Vec` - --> $DIR/box_vec.rs:14:18 + --> $DIR/box_vec.rs:18:14 | -LL | pub fn test(foo: Box>) { - | ^^^^^^^^^^^^^^ +LL | fn test(foo: Box>) {} + | ^^^^^^^^^^^^^^ | = note: `-D clippy::box-vec` implied by `-D warnings` = help: `Vec` is already on the heap, `Box>` makes an extra allocation diff --git a/tests/ui/linkedlist.rs b/tests/ui/linkedlist.rs index 2c3b25cd45e80..690ea810a6214 100644 --- a/tests/ui/linkedlist.rs +++ b/tests/ui/linkedlist.rs @@ -1,6 +1,6 @@ #![feature(associated_type_defaults)] #![warn(clippy::linkedlist)] -#![allow(dead_code, clippy::needless_pass_by_value)] +#![allow(unused, dead_code, clippy::needless_pass_by_value)] extern crate alloc; use alloc::collections::linked_list::LinkedList; @@ -20,24 +20,29 @@ impl Foo for LinkedList { const BAR: Option> = None; } -struct Bar; +pub struct Bar { + priv_linked_list_field: LinkedList, + pub pub_linked_list_field: LinkedList, +} impl Bar { fn foo(_: LinkedList) {} } -pub fn test(my_favourite_linked_list: LinkedList) { - println!("{:?}", my_favourite_linked_list) -} - -pub fn test_ret() -> Option> { - unimplemented!(); +// All of these test should be trigger the lint because they are not +// part of the public api +fn test(my_favorite_linked_list: LinkedList) {} +fn test_ret() -> Option> { + None } - -pub fn test_local_not_linted() { +fn test_local_not_linted() { let _: LinkedList; } -fn main() { - test(LinkedList::new()); - test_local_not_linted(); +// All of these test should be allowed because they are part of the +// public api and `avoid_breaking_exported_api` is `false` by default. +pub fn pub_test(the_most_awesome_linked_list: LinkedList) {} +pub fn pub_test_ret() -> Option> { + None } + +fn main() {} diff --git a/tests/ui/linkedlist.stderr b/tests/ui/linkedlist.stderr index 38ae71714d662..51327df132118 100644 --- a/tests/ui/linkedlist.stderr +++ b/tests/ui/linkedlist.stderr @@ -40,7 +40,15 @@ LL | const BAR: Option>; = help: a `VecDeque` might work error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? - --> $DIR/linkedlist.rs:25:15 + --> $DIR/linkedlist.rs:24:29 + | +LL | priv_linked_list_field: LinkedList, + | ^^^^^^^^^^^^^^ + | + = help: a `VecDeque` might work + +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? + --> $DIR/linkedlist.rs:28:15 | LL | fn foo(_: LinkedList) {} | ^^^^^^^^^^^^^^ @@ -48,20 +56,20 @@ LL | fn foo(_: LinkedList) {} = help: a `VecDeque` might work error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? - --> $DIR/linkedlist.rs:28:39 + --> $DIR/linkedlist.rs:33:34 | -LL | pub fn test(my_favourite_linked_list: LinkedList) { - | ^^^^^^^^^^^^^^ +LL | fn test(my_favorite_linked_list: LinkedList) {} + | ^^^^^^^^^^^^^^ | = help: a `VecDeque` might work error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? - --> $DIR/linkedlist.rs:32:29 + --> $DIR/linkedlist.rs:34:25 | -LL | pub fn test_ret() -> Option> { - | ^^^^^^^^^^^^^^ +LL | fn test_ret() -> Option> { + | ^^^^^^^^^^^^^^ | = help: a `VecDeque` might work -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/rc_mutex.rs b/tests/ui/rc_mutex.rs index 657a3ecf6a006..18e8a2e01e022 100644 --- a/tests/ui/rc_mutex.rs +++ b/tests/ui/rc_mutex.rs @@ -1,13 +1,17 @@ #![warn(clippy::rc_mutex)] -#![allow(clippy::blacklisted_name)] +#![allow(unused, clippy::blacklisted_name)] use std::rc::Rc; use std::sync::Mutex; -pub struct MyStruct { +pub struct MyStructWithPrivItem { foo: Rc>, } +pub struct MyStructWithPubItem { + pub foo: Rc>, +} + pub struct SubT { foo: T, } @@ -17,18 +21,16 @@ pub enum MyEnum { Two, } -pub fn test1(foo: Rc>) {} - -pub fn test2(foo: Rc>) {} +// All of these test should be trigger the lint because they are not +// part of the public api +fn test1(foo: Rc>) {} +fn test2(foo: Rc>) {} +fn test3(foo: Rc>>) {} -pub fn test3(foo: Rc>>) {} +// All of these test should be allowed because they are part of the +// public api and `avoid_breaking_exported_api` is `false` by default. +pub fn pub_test1(foo: Rc>) {} +pub fn pub_test2(foo: Rc>) {} +pub fn pub_test3(foo: Rc>>) {} -fn main() { - test1(Rc::new(Mutex::new(1))); - test2(Rc::new(Mutex::new(MyEnum::One))); - test3(Rc::new(Mutex::new(SubT { foo: 1 }))); - - let _my_struct = MyStruct { - foo: Rc::new(Mutex::new(1)), - }; -} +fn main() {} diff --git a/tests/ui/rc_mutex.stderr b/tests/ui/rc_mutex.stderr index 852e415cf1e2e..fe84361d78162 100644 --- a/tests/ui/rc_mutex.stderr +++ b/tests/ui/rc_mutex.stderr @@ -8,26 +8,26 @@ LL | foo: Rc>, = help: consider using `Rc>` or `Arc>` instead error: usage of `Rc>` - --> $DIR/rc_mutex.rs:20:22 + --> $DIR/rc_mutex.rs:26:18 | -LL | pub fn test1(foo: Rc>) {} - | ^^^^^^^^^^^^ +LL | fn test1(foo: Rc>) {} + | ^^^^^^^^^^^^ | = help: consider using `Rc>` or `Arc>` instead error: usage of `Rc>` - --> $DIR/rc_mutex.rs:22:19 + --> $DIR/rc_mutex.rs:27:15 | -LL | pub fn test2(foo: Rc>) {} - | ^^^^^^^^^^^^^^^^^ +LL | fn test2(foo: Rc>) {} + | ^^^^^^^^^^^^^^^^^ | = help: consider using `Rc>` or `Arc>` instead error: usage of `Rc>` - --> $DIR/rc_mutex.rs:24:19 + --> $DIR/rc_mutex.rs:28:15 | -LL | pub fn test3(foo: Rc>>) {} - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | fn test3(foo: Rc>>) {} + | ^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using `Rc>` or `Arc>` instead From e9f56f949d2d1c11f1331151323beb8e85181c29 Mon Sep 17 00:00:00 2001 From: dswij Date: Fri, 13 Aug 2021 14:34:33 +0800 Subject: [PATCH 07/94] Add false positive test for iterator method --- tests/ui/auxiliary/option_helpers.rs | 9 +++++++++ tests/ui/methods.rs | 5 ++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/ui/auxiliary/option_helpers.rs b/tests/ui/auxiliary/option_helpers.rs index 7dc3f4ebd4d46..86a637ce3093c 100644 --- a/tests/ui/auxiliary/option_helpers.rs +++ b/tests/ui/auxiliary/option_helpers.rs @@ -53,3 +53,12 @@ impl IteratorFalsePositives { self.foo as usize } } + +#[derive(Copy, Clone)] +pub struct IteratorMethodFalsePositives; + +impl IteratorMethodFalsePositives { + pub fn filter(&self, _s: i32) -> std::vec::IntoIter { + unimplemented!(); + } +} diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index 513d930e05687..c441b35b99203 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -32,7 +32,7 @@ use std::ops::Mul; use std::rc::{self, Rc}; use std::sync::{self, Arc}; -use option_helpers::IteratorFalsePositives; +use option_helpers::{IteratorFalsePositives, IteratorMethodFalsePositives}; struct Lt<'a> { foo: &'a u32, @@ -131,6 +131,9 @@ fn filter_next() { // Check that we don't lint if the caller is not an `Iterator`. let foo = IteratorFalsePositives { foo: 0 }; let _ = foo.filter().next(); + + let foo = IteratorMethodFalsePositives {}; + let _ = foo.filter(42).next(); } fn main() { From 91b598a8e484da328325ece3400e8612b6f854df Mon Sep 17 00:00:00 2001 From: dswij Date: Fri, 13 Aug 2021 14:36:40 +0800 Subject: [PATCH 08/94] Fix false positive on `filter_next` --- clippy_lints/src/methods/filter_next.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/methods/filter_next.rs b/clippy_lints/src/methods/filter_next.rs index 172714f6b01c6..bcf8d93b602ef 100644 --- a/clippy_lints/src/methods/filter_next.rs +++ b/clippy_lints/src/methods/filter_next.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; -use clippy_utils::is_trait_method; use clippy_utils::source::snippet; +use clippy_utils::ty::implements_trait; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -16,7 +16,10 @@ pub(super) fn check<'tcx>( filter_arg: &'tcx hir::Expr<'_>, ) { // lint if caller of `.filter().next()` is an Iterator - if is_trait_method(cx, expr, sym::Iterator) { + let recv_impls_iterator = cx.tcx.get_diagnostic_item(sym::Iterator).map_or(false, |id| { + implements_trait(cx, cx.typeck_results().expr_ty(recv), id, &[]) + }); + if recv_impls_iterator { let msg = "called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling \ `.find(..)` instead"; let filter_snippet = snippet(cx, filter_arg.span, ".."); From 80bff87c6f22b98c5a7c0cb36233e4e2ba7e5a56 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Thu, 22 Jul 2021 21:56:07 +0800 Subject: [PATCH 09/94] move Constness into TraitPredicate --- clippy_lints/src/future_not_send.rs | 2 +- clippy_lints/src/needless_pass_by_value.rs | 2 +- clippy_lints/src/unit_return_expecting_ord.rs | 2 +- clippy_utils/src/qualify_min_const_fn.rs | 2 +- clippy_utils/src/ty.rs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index 0be03969bcbe2..3e35ada7b2a1c 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -93,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { cx.tcx.infer_ctxt().enter(|infcx| { for FulfillmentError { obligation, .. } in send_errors { infcx.maybe_note_obligation_cause_for_async_await(db, &obligation); - if let Trait(trait_pred, _) = obligation.predicate.kind().skip_binder() { + if let Trait(trait_pred) = obligation.predicate.kind().skip_binder() { db.note(&format!( "`{}` doesn't implement `{}`", trait_pred.self_ty(), diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 03eeb54d8d1c2..5e559991c1697 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -121,7 +121,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { .filter_map(|obligation| { // Note that we do not want to deal with qualified predicates here. match obligation.predicate.kind().no_bound_vars() { - Some(ty::PredicateKind::Trait(pred, _)) if pred.def_id() != sized_trait => Some(pred), + Some(ty::PredicateKind::Trait(pred)) if pred.def_id() != sized_trait => Some(pred), _ => None, } }) diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs index 900d453176071..ee675838c4cb3 100644 --- a/clippy_lints/src/unit_return_expecting_ord.rs +++ b/clippy_lints/src/unit_return_expecting_ord.rs @@ -45,7 +45,7 @@ fn get_trait_predicates_for_trait_id<'tcx>( let mut preds = Vec::new(); for (pred, _) in generics.predicates { if_chain! { - if let PredicateKind::Trait(poly_trait_pred, _) = pred.kind().skip_binder(); + if let PredicateKind::Trait(poly_trait_pred) = pred.kind().skip_binder(); let trait_pred = cx.tcx.erase_late_bound_regions(pred.kind().rebind(poly_trait_pred)); if let Some(trait_def_id) = trait_id; if trait_def_id == trait_pred.trait_ref.def_id; diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 0e6ead675c247..dee9d487c78ea 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -36,7 +36,7 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&Ru ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate), ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate), ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {:#?}", predicate), - ty::PredicateKind::Trait(pred, _) => { + ty::PredicateKind::Trait(pred) => { if Some(pred.def_id()) == tcx.lang_items().sized_trait() { continue; } diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 4f9aaf396b806..a2221a0b283b0 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -157,7 +157,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { ty::Tuple(substs) => substs.types().any(|ty| is_must_use_ty(cx, ty)), ty::Opaque(ref def_id, _) => { for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) { - if let ty::PredicateKind::Trait(trait_predicate, _) = predicate.kind().skip_binder() { + if let ty::PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder() { if must_use_attr(cx.tcx.get_attrs(trait_predicate.trait_ref.def_id)).is_some() { return true; } From 3c8eaa8b2c2d594d39810fc2f2a62098c4107c80 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 14 Aug 2021 05:47:01 -0700 Subject: [PATCH 10/94] Downgrade option_if_let_else to nursery --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/option_if_let_else.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 18600498e1c46..8372d681078df 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1125,7 +1125,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(needless_for_each::NEEDLESS_FOR_EACH), LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE), LintId::of(non_expressive_names::SIMILAR_NAMES), - LintId::of(option_if_let_else::OPTION_IF_LET_ELSE), LintId::of(pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE), LintId::of(pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF), LintId::of(ranges::RANGE_MINUS_ONE), @@ -1802,6 +1801,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL), LintId::of(mutex_atomic::MUTEX_INTEGER), LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES), + LintId::of(option_if_let_else::OPTION_IF_LET_ELSE), LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE), LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE), LintId::of(regex::TRIVIAL_REGEX), diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 7aef3a5f34cf2..a7899dc8bc126 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -57,7 +57,7 @@ declare_clippy_lint! { /// }, |foo| foo); /// ``` pub OPTION_IF_LET_ELSE, - pedantic, + nursery, "reimplementation of Option::map_or" } From 4838c78ba4ef784379ae6ec5617479de2a32d3f6 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 1 Aug 2021 18:39:56 -0400 Subject: [PATCH 11/94] Improve `manual_map` and `map_entry` Locals which can be partially moved created within the to-be-created closure shouldn't block the use of a closure --- clippy_lints/src/entry.rs | 31 ++++++++++++++------ clippy_utils/src/lib.rs | 44 +++++++++++++++++++++++------ tests/ui/entry.fixed | 15 +++++----- tests/ui/entry.rs | 9 +++--- tests/ui/entry.stderr | 16 ++++++----- tests/ui/entry_btree.fixed | 18 ++++++++++++ tests/ui/entry_btree.rs | 18 ++++++++++++ tests/ui/entry_btree.stderr | 20 +++++++++++++ tests/ui/manual_map_option_2.fixed | 10 +++++++ tests/ui/manual_map_option_2.rs | 13 +++++++++ tests/ui/manual_map_option_2.stderr | 24 ++++++++++++++++ 11 files changed, 181 insertions(+), 37 deletions(-) create mode 100644 tests/ui/entry_btree.fixed create mode 100644 tests/ui/entry_btree.rs create mode 100644 tests/ui/entry_btree.stderr create mode 100644 tests/ui/manual_map_option_2.fixed create mode 100644 tests/ui/manual_map_option_2.rs create mode 100644 tests/ui/manual_map_option_2.stderr diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index e1d0d65edb1b9..7fb8e4276600f 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -7,8 +7,9 @@ use clippy_utils::{ }; use rustc_errors::Applicability; use rustc_hir::{ + hir_id::HirIdSet, intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}, - Block, Expr, ExprKind, Guard, HirId, Local, Stmt, StmtKind, UnOp, + Block, Expr, ExprKind, Guard, HirId, Pat, Stmt, StmtKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -336,6 +337,8 @@ struct InsertSearcher<'cx, 'tcx> { edits: Vec>, /// A stack of loops the visitor is currently in. loops: Vec, + /// Local variables created in the expression. These don't need to be captured. + locals: HirIdSet, } impl<'tcx> InsertSearcher<'_, 'tcx> { /// Visit the expression as a branch in control flow. Multiple insert calls can be used, but @@ -383,13 +386,16 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { } }, StmtKind::Expr(e) => self.visit_expr(e), - StmtKind::Local(Local { init: Some(e), .. }) => { - self.allow_insert_closure &= !self.in_tail_pos; - self.in_tail_pos = false; - self.is_single_insert = false; - self.visit_expr(e); + StmtKind::Local(l) => { + self.visit_pat(l.pat); + if let Some(e) = l.init { + self.allow_insert_closure &= !self.in_tail_pos; + self.in_tail_pos = false; + self.is_single_insert = false; + self.visit_expr(e); + } }, - _ => { + StmtKind::Item(_) => { self.allow_insert_closure &= !self.in_tail_pos; self.is_single_insert = false; }, @@ -471,6 +477,7 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { // Each branch may contain it's own insert expression. let mut is_map_used = self.is_map_used; for arm in arms { + self.visit_pat(arm.pat); if let Some(Guard::If(guard) | Guard::IfLet(_, guard)) = arm.guard { self.visit_non_tail_expr(guard); } @@ -496,7 +503,8 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { }, _ => { self.allow_insert_closure &= !self.in_tail_pos; - self.allow_insert_closure &= can_move_expr_to_closure_no_visit(self.cx, expr, &self.loops); + self.allow_insert_closure &= + can_move_expr_to_closure_no_visit(self.cx, expr, &self.loops, &self.locals); // Sub expressions are no longer in the tail position. self.is_single_insert = false; self.in_tail_pos = false; @@ -505,6 +513,12 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { }, } } + + fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) { + p.each_binding_or_first(&mut |_, id, _, _| { + self.locals.insert(id); + }); + } } struct InsertSearchResults<'tcx> { @@ -630,6 +644,7 @@ fn find_insert_calls( in_tail_pos: true, is_single_insert: true, loops: Vec::new(), + locals: HirIdSet::default(), }; s.visit_expr(expr); let allow_insert_closure = s.allow_insert_closure; diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 1d59d6bfea1b9..aee9f791b039d 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -67,6 +67,7 @@ use rustc_data_structures::unhash::UnhashMap; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; +use rustc_hir::hir_id::HirIdSet; use rustc_hir::intravisit::{self, walk_expr, ErasedMap, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::LangItem::{ResultErr, ResultOk}; use rustc_hir::{ @@ -626,7 +627,12 @@ pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) - } /// Checks if the top level expression can be moved into a closure as is. -pub fn can_move_expr_to_closure_no_visit(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, jump_targets: &[HirId]) -> bool { +pub fn can_move_expr_to_closure_no_visit( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + jump_targets: &[HirId], + ignore_locals: &HirIdSet, +) -> bool { match expr.kind { ExprKind::Break(Destination { target_id: Ok(id), .. }, _) | ExprKind::Continue(Destination { target_id: Ok(id), .. }) @@ -642,15 +648,24 @@ pub fn can_move_expr_to_closure_no_visit(cx: &LateContext<'tcx>, expr: &'tcx Exp | ExprKind::LlvmInlineAsm(_) => false, // Accessing a field of a local value can only be done if the type isn't // partially moved. - ExprKind::Field(base_expr, _) - if matches!( - base_expr.kind, - ExprKind::Path(QPath::Resolved(_, Path { res: Res::Local(_), .. })) - ) && can_partially_move_ty(cx, cx.typeck_results().expr_ty(base_expr)) => - { + ExprKind::Field( + &Expr { + hir_id, + kind: + ExprKind::Path(QPath::Resolved( + _, + Path { + res: Res::Local(local_id), + .. + }, + )), + .. + }, + _, + ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => { // TODO: check if the local has been partially moved. Assume it has for now. false - } + }, _ => true, } } @@ -659,7 +674,11 @@ pub fn can_move_expr_to_closure_no_visit(cx: &LateContext<'tcx>, expr: &'tcx Exp pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { struct V<'cx, 'tcx> { cx: &'cx LateContext<'tcx>, + // Stack of potential break targets contained in the expression. loops: Vec, + /// Local variables created in the expression. These don't need to be captured. + locals: HirIdSet, + /// Whether this expression can be turned into a closure. allow_closure: bool, } impl Visitor<'tcx> for V<'_, 'tcx> { @@ -677,16 +696,23 @@ pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> self.visit_block(b); self.loops.pop(); } else { - self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops); + self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals); walk_expr(self, e); } } + + fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) { + p.each_binding_or_first(&mut |_, id, _, _| { + self.locals.insert(id); + }); + } } let mut v = V { cx, allow_closure: true, loops: Vec::new(), + locals: HirIdSet::default(), }; v.visit_expr(expr); v.allow_closure diff --git a/tests/ui/entry.fixed b/tests/ui/entry.fixed index cfad3090ba38d..8a36ec833d76d 100644 --- a/tests/ui/entry.fixed +++ b/tests/ui/entry.fixed @@ -4,7 +4,7 @@ #![warn(clippy::map_entry)] #![feature(asm)] -use std::collections::{BTreeMap, HashMap}; +use std::collections::HashMap; use std::hash::Hash; macro_rules! m { @@ -142,14 +142,13 @@ fn hash_map(m: &mut HashMap, m2: &mut HashMa if !m.contains_key(&k) { insert!(m, k, v); } -} -fn btree_map(m: &mut BTreeMap, k: K, v: V, v2: V) { - // insert then do something, use if let - if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) { - e.insert(v); - foo(); - } + // or_insert_with. Partial move of a local declared in the closure is ok. + m.entry(k).or_insert_with(|| { + let x = (String::new(), String::new()); + let _ = x.0; + v + }); } fn main() {} diff --git a/tests/ui/entry.rs b/tests/ui/entry.rs index fa9280b58de11..d972a201ad764 100644 --- a/tests/ui/entry.rs +++ b/tests/ui/entry.rs @@ -4,7 +4,7 @@ #![warn(clippy::map_entry)] #![feature(asm)] -use std::collections::{BTreeMap, HashMap}; +use std::collections::HashMap; use std::hash::Hash; macro_rules! m { @@ -146,13 +146,12 @@ fn hash_map(m: &mut HashMap, m2: &mut HashMa if !m.contains_key(&k) { insert!(m, k, v); } -} -fn btree_map(m: &mut BTreeMap, k: K, v: V, v2: V) { - // insert then do something, use if let + // or_insert_with. Partial move of a local declared in the closure is ok. if !m.contains_key(&k) { + let x = (String::new(), String::new()); + let _ = x.0; m.insert(k, v); - foo(); } } diff --git a/tests/ui/entry.stderr b/tests/ui/entry.stderr index 8f2e383d675da..1076500498d32 100644 --- a/tests/ui/entry.stderr +++ b/tests/ui/entry.stderr @@ -165,21 +165,23 @@ LL | | m.insert(m!(k), m!(v)); LL | | } | |_____^ help: try this: `m.entry(m!(k)).or_insert_with(|| m!(v));` -error: usage of `contains_key` followed by `insert` on a `BTreeMap` - --> $DIR/entry.rs:153:5 +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry.rs:151:5 | LL | / if !m.contains_key(&k) { +LL | | let x = (String::new(), String::new()); +LL | | let _ = x.0; LL | | m.insert(k, v); -LL | | foo(); LL | | } | |_____^ | help: try this | -LL ~ if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) { -LL + e.insert(v); -LL + foo(); -LL + } +LL ~ m.entry(k).or_insert_with(|| { +LL + let x = (String::new(), String::new()); +LL + let _ = x.0; +LL + v +LL + }); | error: aborting due to 10 previous errors diff --git a/tests/ui/entry_btree.fixed b/tests/ui/entry_btree.fixed new file mode 100644 index 0000000000000..94979104556bc --- /dev/null +++ b/tests/ui/entry_btree.fixed @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::map_entry)] +#![allow(dead_code)] + +use std::collections::BTreeMap; + +fn foo() {} + +fn btree_map(m: &mut BTreeMap, k: K, v: V) { + // insert then do something, use if let + if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) { + e.insert(v); + foo(); + } +} + +fn main() {} diff --git a/tests/ui/entry_btree.rs b/tests/ui/entry_btree.rs new file mode 100644 index 0000000000000..080c1d959e894 --- /dev/null +++ b/tests/ui/entry_btree.rs @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::map_entry)] +#![allow(dead_code)] + +use std::collections::BTreeMap; + +fn foo() {} + +fn btree_map(m: &mut BTreeMap, k: K, v: V) { + // insert then do something, use if let + if !m.contains_key(&k) { + m.insert(k, v); + foo(); + } +} + +fn main() {} diff --git a/tests/ui/entry_btree.stderr b/tests/ui/entry_btree.stderr new file mode 100644 index 0000000000000..5c6fcdf1a28c0 --- /dev/null +++ b/tests/ui/entry_btree.stderr @@ -0,0 +1,20 @@ +error: usage of `contains_key` followed by `insert` on a `BTreeMap` + --> $DIR/entry_btree.rs:12:5 + | +LL | / if !m.contains_key(&k) { +LL | | m.insert(k, v); +LL | | foo(); +LL | | } + | |_____^ + | + = note: `-D clippy::map-entry` implied by `-D warnings` +help: try this + | +LL ~ if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) { +LL + e.insert(v); +LL + foo(); +LL + } + | + +error: aborting due to previous error + diff --git a/tests/ui/manual_map_option_2.fixed b/tests/ui/manual_map_option_2.fixed new file mode 100644 index 0000000000000..147a0c49e6a06 --- /dev/null +++ b/tests/ui/manual_map_option_2.fixed @@ -0,0 +1,10 @@ +// run-rustfix + +#![warn(clippy::manual_map)] + +fn main() { + let _ = Some(0).map(|x| { + let y = (String::new(), String::new()); + (x, y.0) + }); +} diff --git a/tests/ui/manual_map_option_2.rs b/tests/ui/manual_map_option_2.rs new file mode 100644 index 0000000000000..cc612dcc601da --- /dev/null +++ b/tests/ui/manual_map_option_2.rs @@ -0,0 +1,13 @@ +// run-rustfix + +#![warn(clippy::manual_map)] + +fn main() { + let _ = match Some(0) { + Some(x) => Some({ + let y = (String::new(), String::new()); + (x, y.0) + }), + None => None, + }; +} diff --git a/tests/ui/manual_map_option_2.stderr b/tests/ui/manual_map_option_2.stderr new file mode 100644 index 0000000000000..745737079f377 --- /dev/null +++ b/tests/ui/manual_map_option_2.stderr @@ -0,0 +1,24 @@ +error: manual implementation of `Option::map` + --> $DIR/manual_map_option_2.rs:6:13 + | +LL | let _ = match Some(0) { + | _____________^ +LL | | Some(x) => Some({ +LL | | let y = (String::new(), String::new()); +LL | | (x, y.0) +LL | | }), +LL | | None => None, +LL | | }; + | |_____^ + | + = note: `-D clippy::manual-map` implied by `-D warnings` +help: try this + | +LL | let _ = Some(0).map(|x| { +LL | let y = (String::new(), String::new()); +LL | (x, y.0) +LL | }); + | + +error: aborting due to previous error + From 251dd30d77c98cbebd1c68840fce029affe9b6a8 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 4 Aug 2021 13:48:45 -0400 Subject: [PATCH 12/94] Improve `manual_map` In some cases check if a borrow made in the scrutinee expression would prevent creating the closure used by `map` --- clippy_lints/src/manual_map.rs | 36 +++++++-- clippy_utils/src/lib.rs | 117 +++++++++++++++++++++++++---- tests/ui/manual_map_option_2.fixed | 6 ++ tests/ui/manual_map_option_2.rs | 6 ++ 4 files changed, 145 insertions(+), 20 deletions(-) diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index 7dec1595e0d10..3ea88f52a1ccf 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -4,12 +4,14 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; use clippy_utils::{ can_move_expr_to_closure, in_constant, is_else_clause, is_lang_ctor, is_lint_allowed, path_to_local_id, - peel_hir_expr_refs, + peel_hir_expr_refs, peel_hir_expr_while, CaptureKind, }; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; -use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, HirId, MatchSource, Mutability, Pat, PatKind}; +use rustc_hir::{ + def::Res, Arm, BindingAnnotation, Block, Expr, ExprKind, HirId, MatchSource, Mutability, Pat, PatKind, Path, QPath, +}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -111,10 +113,6 @@ impl LateLintPass<'_> for ManualMap { return; } - if !can_move_expr_to_closure(cx, some_expr) { - return; - } - // Determine which binding mode to use. let explicit_ref = some_pat.contains_explicit_ref_binding(); let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then(|| ty_mutability)); @@ -125,6 +123,32 @@ impl LateLintPass<'_> for ManualMap { None => "", }; + match can_move_expr_to_closure(cx, some_expr) { + Some(captures) => { + // Check if captures the closure will need conflict with borrows made in the scrutinee. + // TODO: check all the references made in the scrutinee expression. This will require interacting + // with the borrow checker. Currently only `[.]*` is checked for. + if let Some(binding_ref_mutability) = binding_ref { + let e = peel_hir_expr_while(scrutinee, |e| match e.kind { + ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e), + _ => None, + }); + if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind { + match captures.get(l) { + Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return, + Some(CaptureKind::Ref(Mutability::Not)) + if binding_ref_mutability == Mutability::Mut => + { + return; + } + Some(CaptureKind::Ref(Mutability::Not)) | None => (), + } + } + } + }, + None => return, + }; + let mut app = Applicability::MachineApplicable; // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index aee9f791b039d..2655ba6a8e9dd 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -67,19 +67,20 @@ use rustc_data_structures::unhash::UnhashMap; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::hir_id::HirIdSet; +use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{self, walk_expr, ErasedMap, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::LangItem::{ResultErr, ResultOk}; use rustc_hir::{ def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, - ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Node, Param, Pat, PatKind, Path, - PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp, + ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Mutability, Node, Param, Pat, + PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp, }; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::exports::Export; use rustc_middle::hir::map::Map; use rustc_middle::ty as rustc_ty; -use rustc_middle::ty::{layout::IntegerExt, DefIdTree, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; +use rustc_middle::ty::{layout::IntegerExt, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeFoldable}; use rustc_semver::RustcVersion; use rustc_session::Session; use rustc_span::hygiene::{ExpnKind, MacroKind}; @@ -670,8 +671,82 @@ pub fn can_move_expr_to_closure_no_visit( } } -/// Checks if the expression can be moved into a closure as is. -pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { +/// How a local is captured by a closure +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum CaptureKind { + Value, + Ref(Mutability), +} +impl std::ops::BitOr for CaptureKind { + type Output = Self; + fn bitor(self, rhs: Self) -> Self::Output { + match (self, rhs) { + (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value, + (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_)) + | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut), + (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not), + } + } +} +impl std::ops::BitOrAssign for CaptureKind { + fn bitor_assign(&mut self, rhs: Self) { + *self = *self | rhs; + } +} + +/// Given an expression referencing a local, determines how it would be captured in a closure. +/// Note as this will walk up to parent expressions until the capture can be determined it should +/// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or +/// function argument (other than a receiver). +pub fn capture_local_usage(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind { + debug_assert!(matches!( + e.kind, + ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. })) + )); + + let map = cx.tcx.hir(); + let mut child_id = e.hir_id; + let mut capture = CaptureKind::Value; + + for (parent_id, parent) in map.parent_iter(e.hir_id) { + if let [Adjustment { + kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)), + target, + }, ref adjust @ ..] = *cx + .typeck_results() + .adjustments() + .get(child_id) + .map_or(&[][..], |x| &**x) + { + if let rustc_ty::RawPtr(TypeAndMut { mutbl: mutability, .. }) | rustc_ty::Ref(_, _, mutability) = + *adjust.last().map_or(target, |a| a.target).kind() + { + return CaptureKind::Ref(mutability); + } + } + + if let Node::Expr(e) = parent { + match e.kind { + ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability), + ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not), + ExprKind::Assign(lhs, ..) | ExprKind::Assign(_, lhs, _) if lhs.hir_id == child_id => { + return CaptureKind::Ref(Mutability::Mut); + }, + _ => break, + } + } else { + break; + } + + child_id = parent_id; + } + + capture +} + +/// Checks if the expression can be moved into a closure as is. This will return a list of captures +/// if so, otherwise, `None`. +pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { struct V<'cx, 'tcx> { cx: &'cx LateContext<'tcx>, // Stack of potential break targets contained in the expression. @@ -680,6 +755,9 @@ pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> locals: HirIdSet, /// Whether this expression can be turned into a closure. allow_closure: bool, + /// Locals which need to be captured, and whether they need to be by value, reference, or + /// mutable reference. + captures: HirIdMap, } impl Visitor<'tcx> for V<'_, 'tcx> { type Map = ErasedMap<'tcx>; @@ -691,13 +769,23 @@ pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> if !self.allow_closure { return; } - if let ExprKind::Loop(b, ..) = e.kind { - self.loops.push(e.hir_id); - self.visit_block(b); - self.loops.pop(); - } else { - self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals); - walk_expr(self, e); + + match e.kind { + ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => { + if !self.locals.contains(&l) { + let cap = capture_local_usage(self.cx, e); + self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap); + } + }, + ExprKind::Loop(b, ..) => { + self.loops.push(e.hir_id); + self.visit_block(b); + self.loops.pop(); + }, + _ => { + self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals); + walk_expr(self, e); + }, } } @@ -713,9 +801,10 @@ pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> allow_closure: true, loops: Vec::new(), locals: HirIdSet::default(), + captures: HirIdMap::default(), }; v.visit_expr(expr); - v.allow_closure + v.allow_closure.then(|| v.captures) } /// Returns the method names and argument list of nested method call expressions that make up diff --git a/tests/ui/manual_map_option_2.fixed b/tests/ui/manual_map_option_2.fixed index 147a0c49e6a06..637f032795437 100644 --- a/tests/ui/manual_map_option_2.fixed +++ b/tests/ui/manual_map_option_2.fixed @@ -7,4 +7,10 @@ fn main() { let y = (String::new(), String::new()); (x, y.0) }); + + let s = Some(String::new()); + let _ = match &s { + Some(x) => Some((x.clone(), s)), + None => None, + }; } diff --git a/tests/ui/manual_map_option_2.rs b/tests/ui/manual_map_option_2.rs index cc612dcc601da..98e00604a1b3d 100644 --- a/tests/ui/manual_map_option_2.rs +++ b/tests/ui/manual_map_option_2.rs @@ -10,4 +10,10 @@ fn main() { }), None => None, }; + + let s = Some(String::new()); + let _ = match &s { + Some(x) => Some((x.clone(), s)), + None => None, + }; } From 9500974bdb5f7cd9a8ba056b41bd5af5f373e0d3 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 9 Aug 2021 14:18:53 -0400 Subject: [PATCH 13/94] Fix tracking of which locals would need to be captured in a closure. * Captures by sub closures are now considered * Copy types are correctly borrowed by reference when their value is used * Fields are no longer automatically borrowed by value * Bindings in `match` and `let` patterns are now checked to determine how a local is captured --- clippy_utils/src/lib.rs | 95 +++++++++++++++++++++++++---- tests/ui/manual_map_option_2.fixed | 34 +++++++++++ tests/ui/manual_map_option_2.rs | 37 +++++++++++ tests/ui/manual_map_option_2.stderr | 23 ++++++- 4 files changed, 176 insertions(+), 13 deletions(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 2655ba6a8e9dd..eb9ad04527f42 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -62,7 +62,7 @@ use std::collections::hash_map::Entry; use std::hash::BuildHasherDefault; use if_chain::if_chain; -use rustc_ast::ast::{self, Attribute, BorrowKind, LitKind}; +use rustc_ast::ast::{self, Attribute, LitKind}; use rustc_data_structures::unhash::UnhashMap; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; @@ -78,9 +78,11 @@ use rustc_hir::{ use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::exports::Export; use rustc_middle::hir::map::Map; +use rustc_middle::hir::place::PlaceBase; use rustc_middle::ty as rustc_ty; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; -use rustc_middle::ty::{layout::IntegerExt, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeFoldable}; +use rustc_middle::ty::binding::BindingMode; +use rustc_middle::ty::{layout::IntegerExt, BorrowKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeFoldable, UpvarCapture}; use rustc_semver::RustcVersion; use rustc_session::Session; use rustc_span::hygiene::{ExpnKind, MacroKind}; @@ -91,7 +93,7 @@ use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::Integer; use crate::consts::{constant, Constant}; -use crate::ty::{can_partially_move_ty, is_recursively_primitive_type}; +use crate::ty::{can_partially_move_ty, is_copy, is_recursively_primitive_type}; pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Option { if let Ok(version) = RustcVersion::parse(msrv) { @@ -628,6 +630,11 @@ pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) - } /// Checks if the top level expression can be moved into a closure as is. +/// Currently checks for: +/// * Break/Continue outside the given jump targets +/// * Yield/Return statments. +/// * Inline assembly +/// * Usages of a field of a local where the type of the local can be partially moved. pub fn can_move_expr_to_closure_no_visit( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, @@ -699,6 +706,22 @@ impl std::ops::BitOrAssign for CaptureKind { /// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or /// function argument (other than a receiver). pub fn capture_local_usage(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind { + fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind { + let mut capture = CaptureKind::Ref(Mutability::Not); + pat.each_binding(|_, id, span, _| { + match cx.typeck_results().extract_binding_mode(cx.sess(), id, span).unwrap() { + BindingMode::BindByValue(_) if !is_copy(cx, cx.typeck_results().node_type(id)) => { + capture = CaptureKind::Value; + }, + BindingMode::BindByReference(Mutability::Mut) if capture != CaptureKind::Value => { + capture = CaptureKind::Ref(Mutability::Mut); + }, + _ => (), + } + }); + capture + } + debug_assert!(matches!( e.kind, ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. })) @@ -707,6 +730,7 @@ pub fn capture_local_usage(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind let map = cx.tcx.hir(); let mut child_id = e.hir_id; let mut capture = CaptureKind::Value; + let mut capture_expr_ty = e; for (parent_id, parent) in map.parent_iter(e.hir_id) { if let [Adjustment { @@ -725,23 +749,47 @@ pub fn capture_local_usage(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind } } - if let Node::Expr(e) = parent { - match e.kind { + match parent { + Node::Expr(e) => match e.kind { ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability), ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not), ExprKind::Assign(lhs, ..) | ExprKind::Assign(_, lhs, _) if lhs.hir_id == child_id => { return CaptureKind::Ref(Mutability::Mut); }, + ExprKind::Field(..) => { + if capture == CaptureKind::Value { + capture_expr_ty = e; + } + }, + ExprKind::Match(_, arms, _) => { + let mut mutability = Mutability::Not; + for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) { + match capture { + CaptureKind::Value => break, + CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut, + CaptureKind::Ref(Mutability::Not) => (), + } + } + return CaptureKind::Ref(mutability); + }, _ => break, - } - } else { - break; + }, + Node::Local(l) => match pat_capture_kind(cx, l.pat) { + CaptureKind::Value => break, + capture @ CaptureKind::Ref(_) => return capture, + }, + _ => break, } child_id = parent_id; } - capture + if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) { + // Copy types are never automatically captured by value. + CaptureKind::Ref(Mutability::Not) + } else { + capture + } } /// Checks if the expression can be moved into a closure as is. This will return a list of captures @@ -777,6 +825,31 @@ pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap); } }, + ExprKind::Closure(..) => { + let closure_id = self.cx.tcx.hir().local_def_id(e.hir_id).to_def_id(); + for capture in self.cx.typeck_results().closure_min_captures_flattened(closure_id) { + let local_id = match capture.place.base { + PlaceBase::Local(id) => id, + PlaceBase::Upvar(var) => var.var_path.hir_id, + _ => continue, + }; + if !self.locals.contains(&local_id) { + let capture = match capture.info.capture_kind { + UpvarCapture::ByValue(_) => CaptureKind::Value, + UpvarCapture::ByRef(borrow) => match borrow.kind { + BorrowKind::ImmBorrow => CaptureKind::Ref(Mutability::Not), + BorrowKind::UniqueImmBorrow | BorrowKind::MutBorrow => { + CaptureKind::Ref(Mutability::Mut) + }, + }, + }; + self.captures + .entry(local_id) + .and_modify(|e| *e |= capture) + .or_insert(capture); + } + } + }, ExprKind::Loop(b, ..) => { self.loops.push(e.hir_id); self.visit_block(b); @@ -1830,7 +1903,7 @@ pub fn peel_hir_expr_while<'tcx>( pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) { let mut remaining = count; let e = peel_hir_expr_while(expr, |e| match e.kind { - ExprKind::AddrOf(BorrowKind::Ref, _, e) if remaining != 0 => { + ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => { remaining -= 1; Some(e) }, @@ -1844,7 +1917,7 @@ pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, pub fn peel_hir_expr_refs(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) { let mut count = 0; let e = peel_hir_expr_while(expr, |e| match e.kind { - ExprKind::AddrOf(BorrowKind::Ref, _, e) => { + ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => { count += 1; Some(e) }, diff --git a/tests/ui/manual_map_option_2.fixed b/tests/ui/manual_map_option_2.fixed index 637f032795437..8cc12149403d3 100644 --- a/tests/ui/manual_map_option_2.fixed +++ b/tests/ui/manual_map_option_2.fixed @@ -1,16 +1,50 @@ // run-rustfix #![warn(clippy::manual_map)] +#![allow(clippy::toplevel_ref_arg)] fn main() { + // Lint. `y` is declared within the arm, so it isn't captured by the map closure let _ = Some(0).map(|x| { let y = (String::new(), String::new()); (x, y.0) }); + // Don't lint. `s` is borrowed until partway through the arm, but needs to be captured by the map + // closure let s = Some(String::new()); let _ = match &s { Some(x) => Some((x.clone(), s)), None => None, }; + + // Don't lint. `s` is borrowed until partway through the arm, but needs to be captured by the map + // closure + let s = Some(String::new()); + let _ = match &s { + Some(x) => Some({ + let clone = x.clone(); + let s = || s; + (clone, s()) + }), + None => None, + }; + + // Don't lint. `s` is borrowed until partway through the arm, but needs to be captured as a mutable + // reference by the map closure + let mut s = Some(String::new()); + let _ = match &s { + Some(x) => Some({ + let clone = x.clone(); + let ref mut s = s; + (clone, s) + }), + None => None, + }; + + // Lint. `s` is captured by reference, so no lifetime issues. + let s = Some(String::new()); + let _ = s.as_ref().map(|x| { + if let Some(ref s) = s { (x.clone(), s) } else { panic!() } + }); } diff --git a/tests/ui/manual_map_option_2.rs b/tests/ui/manual_map_option_2.rs index 98e00604a1b3d..0862b201ead4b 100644 --- a/tests/ui/manual_map_option_2.rs +++ b/tests/ui/manual_map_option_2.rs @@ -1,8 +1,10 @@ // run-rustfix #![warn(clippy::manual_map)] +#![allow(clippy::toplevel_ref_arg)] fn main() { + // Lint. `y` is declared within the arm, so it isn't captured by the map closure let _ = match Some(0) { Some(x) => Some({ let y = (String::new(), String::new()); @@ -11,9 +13,44 @@ fn main() { None => None, }; + // Don't lint. `s` is borrowed until partway through the arm, but needs to be captured by the map + // closure let s = Some(String::new()); let _ = match &s { Some(x) => Some((x.clone(), s)), None => None, }; + + // Don't lint. `s` is borrowed until partway through the arm, but needs to be captured by the map + // closure + let s = Some(String::new()); + let _ = match &s { + Some(x) => Some({ + let clone = x.clone(); + let s = || s; + (clone, s()) + }), + None => None, + }; + + // Don't lint. `s` is borrowed until partway through the arm, but needs to be captured as a mutable + // reference by the map closure + let mut s = Some(String::new()); + let _ = match &s { + Some(x) => Some({ + let clone = x.clone(); + let ref mut s = s; + (clone, s) + }), + None => None, + }; + + // Lint. `s` is captured by reference, so no lifetime issues. + let s = Some(String::new()); + let _ = match &s { + Some(x) => Some({ + if let Some(ref s) = s { (x.clone(), s) } else { panic!() } + }), + None => None, + }; } diff --git a/tests/ui/manual_map_option_2.stderr b/tests/ui/manual_map_option_2.stderr index 745737079f377..9cfd83f445b15 100644 --- a/tests/ui/manual_map_option_2.stderr +++ b/tests/ui/manual_map_option_2.stderr @@ -1,5 +1,5 @@ error: manual implementation of `Option::map` - --> $DIR/manual_map_option_2.rs:6:13 + --> $DIR/manual_map_option_2.rs:8:13 | LL | let _ = match Some(0) { | _____________^ @@ -20,5 +20,24 @@ LL | (x, y.0) LL | }); | -error: aborting due to previous error +error: manual implementation of `Option::map` + --> $DIR/manual_map_option_2.rs:50:13 + | +LL | let _ = match &s { + | _____________^ +LL | | Some(x) => Some({ +LL | | if let Some(ref s) = s { (x.clone(), s) } else { panic!() } +LL | | }), +LL | | None => None, +LL | | }; + | |_____^ + | +help: try this + | +LL | let _ = s.as_ref().map(|x| { +LL | if let Some(ref s) = s { (x.clone(), s) } else { panic!() } +LL | }); + | + +error: aborting due to 2 previous errors From 5e4d8b44f9524f28fd1ebca18f48ecad204cf184 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 9 Aug 2021 16:26:27 -0400 Subject: [PATCH 14/94] Improve doc for `can_move_expr_to_closure_no_visit` --- clippy_utils/src/lib.rs | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index eb9ad04527f42..b40b42fa6d3b6 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -631,20 +631,45 @@ pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) - /// Checks if the top level expression can be moved into a closure as is. /// Currently checks for: -/// * Break/Continue outside the given jump targets +/// * Break/Continue outside the given loop HIR ids. /// * Yield/Return statments. -/// * Inline assembly +/// * Inline assembly. /// * Usages of a field of a local where the type of the local can be partially moved. +/// +/// For example, given the following function: +/// +/// ``` +/// fn f<'a>(iter: &mut impl Iterator) { +/// for item in iter { +/// let s = item.1; +/// if item.0 > 10 { +/// continue; +/// } else { +/// s.clear(); +/// } +/// } +/// } +/// ``` +/// +/// When called on the expression `item.0` this will return false unless the local `item` is in the +/// `ignore_locals` set. The type `(usize, &mut String)` can have the second element moved, so it +/// isn't always safe to move into a closure when only a single field is needed. +/// +/// When called on the `continue` expression this will return false unless the outer loop expression +/// is in the `loop_ids` set. +/// +/// Note that this check is not recursive, so passing the `if` expression will always return true +/// even though sub-expressions might return false. pub fn can_move_expr_to_closure_no_visit( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, - jump_targets: &[HirId], + loop_ids: &[HirId], ignore_locals: &HirIdSet, ) -> bool { match expr.kind { ExprKind::Break(Destination { target_id: Ok(id), .. }, _) | ExprKind::Continue(Destination { target_id: Ok(id), .. }) - if jump_targets.contains(&id) => + if loop_ids.contains(&id) => { true }, From 10c0460a471d03a633cd15367bf0a85c007e0219 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 14 Aug 2021 19:52:59 -0400 Subject: [PATCH 15/94] update stderr messages --- tests/ui/manual_map_option_2.stderr | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/ui/manual_map_option_2.stderr b/tests/ui/manual_map_option_2.stderr index 9cfd83f445b15..711ff6c4a4b09 100644 --- a/tests/ui/manual_map_option_2.stderr +++ b/tests/ui/manual_map_option_2.stderr @@ -14,10 +14,10 @@ LL | | }; = note: `-D clippy::manual-map` implied by `-D warnings` help: try this | -LL | let _ = Some(0).map(|x| { -LL | let y = (String::new(), String::new()); -LL | (x, y.0) -LL | }); +LL ~ let _ = Some(0).map(|x| { +LL + let y = (String::new(), String::new()); +LL + (x, y.0) +LL ~ }); | error: manual implementation of `Option::map` @@ -34,9 +34,9 @@ LL | | }; | help: try this | -LL | let _ = s.as_ref().map(|x| { -LL | if let Some(ref s) = s { (x.clone(), s) } else { panic!() } -LL | }); +LL ~ let _ = s.as_ref().map(|x| { +LL + if let Some(ref s) = s { (x.clone(), s) } else { panic!() } +LL ~ }); | error: aborting due to 2 previous errors From b97d4c062b4d99139f14960daa7ce99d63625833 Mon Sep 17 00:00:00 2001 From: Caio Date: Sun, 8 Aug 2021 11:49:13 -0300 Subject: [PATCH 16/94] Introduce hir::ExprKind::Let - Take 2 --- clippy_lints/src/assertions_on_constants.rs | 5 +- clippy_lints/src/blocks_in_if_conditions.rs | 3 +- clippy_lints/src/collapsible_match.rs | 77 ++- clippy_lints/src/copies.rs | 17 +- clippy_lints/src/dereference.rs | 1 + clippy_lints/src/entry.rs | 8 +- clippy_lints/src/eta_reduction.rs | 2 +- clippy_lints/src/floating_point_arithmetic.rs | 7 +- clippy_lints/src/if_let_mutex.rs | 27 +- clippy_lints/src/if_let_some_result.rs | 15 +- clippy_lints/src/if_then_some_else_none.rs | 4 +- clippy_lints/src/implicit_saturating_sub.rs | 3 +- clippy_lints/src/indexing_slicing.rs | 2 +- clippy_lints/src/infinite_iter.rs | 2 +- clippy_lints/src/let_if_seq.rs | 4 +- clippy_lints/src/loops/manual_flatten.rs | 13 +- clippy_lints/src/loops/manual_memcpy.rs | 2 +- clippy_lints/src/loops/mod.rs | 6 +- clippy_lints/src/loops/mut_range_bound.rs | 2 +- clippy_lints/src/loops/needless_range_loop.rs | 2 +- clippy_lints/src/loops/never_loop.rs | 9 +- clippy_lints/src/loops/while_let_loop.rs | 98 ++-- .../src/loops/while_let_on_iterator.rs | 25 +- clippy_lints/src/manual_map.rs | 301 ++++++----- clippy_lints/src/manual_strip.rs | 4 +- clippy_lints/src/matches.rs | 265 +++++---- clippy_lints/src/methods/iter_next_slice.rs | 4 +- clippy_lints/src/mut_mut.rs | 2 +- clippy_lints/src/needless_bool.rs | 14 +- clippy_lints/src/non_expressive_names.rs | 3 +- clippy_lints/src/option_if_let_else.rs | 43 +- clippy_lints/src/pattern_type_mismatch.rs | 33 +- clippy_lints/src/question_mark.rs | 27 +- clippy_lints/src/ranges.rs | 14 +- clippy_lints/src/redundant_clone.rs | 2 +- clippy_lints/src/returns.rs | 8 - .../src/suspicious_operation_groupings.rs | 2 +- clippy_lints/src/unnested_or_patterns.rs | 2 +- clippy_lints/src/unwrap.rs | 7 +- clippy_lints/src/utils/author.rs | 9 + clippy_lints/src/utils/inspector.rs | 33 +- clippy_lints/src/vec.rs | 8 +- clippy_utils/src/ast_utils.rs | 2 +- clippy_utils/src/eager_or_lazy.rs | 1 + clippy_utils/src/higher.rs | 507 +++++++++++++----- clippy_utils/src/hir_utils.rs | 7 + clippy_utils/src/lib.rs | 19 +- clippy_utils/src/sugg.rs | 3 +- tests/ui/author/if.stdout | 3 +- tests/ui/collapsible_match.stderr | 8 +- tests/ui/crashes/ice-7410.rs | 1 + tests/ui/crashes/issues_loop_mut_cond.rs | 1 + tests/ui/infinite_loop.rs | 2 + tests/ui/infinite_loop.stderr | 22 +- tests/ui/match_overlapping_arm.rs | 1 + tests/ui/match_overlapping_arm.stderr | 20 +- 56 files changed, 1043 insertions(+), 669 deletions(-) diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index cb9347a923d87..891e865b245dd 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -1,5 +1,6 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::higher; use clippy_utils::source::snippet_opt; use clippy_utils::{is_direct_expn_of, is_expn_of, match_panic_call}; use if_chain::if_chain; @@ -116,8 +117,8 @@ enum AssertKind { /// where `message` is any expression and `c` is a constant bool. fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option { if_chain! { - if let ExprKind::If(cond, then, _) = expr.kind; - if let ExprKind::Unary(UnOp::Not, expr) = cond.kind; + if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr); + if let ExprKind::Unary(UnOp::Not, ref expr) = cond.kind; // bind the first argument of the `assert!` macro if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), expr); // block diff --git a/clippy_lints/src/blocks_in_if_conditions.rs b/clippy_lints/src/blocks_in_if_conditions.rs index 9b2e4f8998e4e..51d95cc6f0b10 100644 --- a/clippy_lints/src/blocks_in_if_conditions.rs +++ b/clippy_lints/src/blocks_in_if_conditions.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::higher; use clippy_utils::source::snippet_block_with_applicability; use clippy_utils::ty::implements_trait; use clippy_utils::{differing_macro_contexts, get_parent_expr}; @@ -92,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInIfConditions { if in_external_macro(cx.sess(), expr.span) { return; } - if let ExprKind::If(cond, _, _) = &expr.kind { + if let Some(higher::If { cond, .. }) = higher::If::hir(expr) { if let ExprKind::Block(block, _) = &cond.kind { if block.rules == BlockCheckMode::DefaultBlock { if block.stmts.is_empty() { diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/collapsible_match.rs index a403a9846babd..6b63c2cf157ac 100644 --- a/clippy_lints/src/collapsible_match.rs +++ b/clippy_lints/src/collapsible_match.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::visitors::LocalUsedVisitor; -use clippy_utils::{is_lang_ctor, path_to_local, peel_ref_operators, SpanlessEq}; +use clippy_utils::{higher, is_lang_ctor, path_to_local, peel_ref_operators, SpanlessEq}; use if_chain::if_chain; use rustc_hir::LangItem::OptionNone; -use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind}; +use rustc_hir::{Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{MultiSpan, Span}; @@ -49,22 +49,44 @@ declare_lint_pass!(CollapsibleMatch => [COLLAPSIBLE_MATCH]); impl<'tcx> LateLintPass<'tcx> for CollapsibleMatch { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { + if let Some(higher::IfLet { + let_pat, + if_then, + if_else, + .. + }) = higher::IfLet::hir(expr) + { + check_arm(cx, if_then, None, let_pat, if_else); + + check_if_let(cx, if_then, let_pat); + } + if let ExprKind::Match(_expr, arms, _source) = expr.kind { - if let Some(wild_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) { + if let Some(wild_arm) = arms.iter().rfind(|arm| is_wild_like(cx, &arm.pat.kind, &arm.guard)) { for arm in arms { - check_arm(arm, wild_arm, cx); + check_arm(cx, arm.body, arm.guard.as_ref(), arm.pat, Some(wild_arm.body)); } } + + if let Some(first_arm) = arms.get(0) { + check_if_let(cx, &first_arm.body, &first_arm.pat); + } } } } -fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext<'tcx>) { - let expr = strip_singleton_blocks(arm.body); +fn check_arm<'tcx>( + cx: &LateContext<'tcx>, + outer_block: &'tcx Expr<'tcx>, + outer_guard: Option<&Guard<'tcx>>, + outer_pat: &'tcx Pat<'tcx>, + wild_outer_block: Option<&'tcx Expr<'tcx>>, +) { + let expr = strip_singleton_blocks(outer_block); if_chain! { if let ExprKind::Match(expr_in, arms_inner, _) = expr.kind; // the outer arm pattern and the inner match - if expr_in.span.ctxt() == arm.pat.span.ctxt(); + if expr_in.span.ctxt() == outer_pat.span.ctxt(); // there must be no more than two arms in the inner match for this lint if arms_inner.len() == 2; // no if guards on the inner match @@ -73,18 +95,18 @@ fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext // match { .. } if let Some(binding_id) = path_to_local(peel_ref_operators(cx, expr_in)); // one of the branches must be "wild-like" - if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(cx, arm_inner)); + if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| is_wild_like(cx, &arm_inner.pat.kind, &arm_inner.guard)); let (wild_inner_arm, non_wild_inner_arm) = (&arms_inner[wild_inner_arm_idx], &arms_inner[1 - wild_inner_arm_idx]); if !pat_contains_or(non_wild_inner_arm.pat); // the binding must come from the pattern of the containing match arm // .... => match { .. } - if let Some(binding_span) = find_pat_binding(arm.pat, binding_id); + if let Some(binding_span) = find_pat_binding(outer_pat, binding_id); // the "wild-like" branches must be equal - if SpanlessEq::new(cx).eq_expr(wild_inner_arm.body, wild_outer_arm.body); + if wild_outer_block.map(|el| SpanlessEq::new(cx).eq_expr(wild_inner_arm.body, el)).unwrap_or(true); // the binding must not be used in the if guard let mut used_visitor = LocalUsedVisitor::new(cx, binding_id); - if match arm.guard { + if match outer_guard { None => true, Some(Guard::If(expr) | Guard::IfLet(_, expr)) => !used_visitor.check_expr(expr), }; @@ -107,6 +129,31 @@ fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext } } +fn check_if_let<'tcx>(cx: &LateContext<'tcx>, outer_expr: &'tcx Expr<'tcx>, outer_pat: &'tcx Pat<'tcx>) { + let block_inner = strip_singleton_blocks(outer_expr); + if_chain! { + if let Some(higher::IfLet { if_then: inner_if_then, let_expr: inner_let_expr, let_pat: inner_let_pat, .. }) = higher::IfLet::hir(block_inner); + if let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_let_expr)); + if let Some(binding_span) = find_pat_binding(outer_pat, binding_id); + let mut used_visitor = LocalUsedVisitor::new(cx, binding_id); + if !used_visitor.check_expr(inner_if_then); + then { + span_lint_and_then( + cx, + COLLAPSIBLE_MATCH, + block_inner.span, + "unnecessary nested `if let` or `match`", + |diag| { + let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_let_pat.span]); + help_span.push_span_label(binding_span, "replace this binding".into()); + help_span.push_span_label(inner_let_pat.span, "with this pattern".into()); + diag.span_help(help_span, "the outer pattern can be modified to include the inner pattern"); + }, + ); + } + } +} + fn strip_singleton_blocks<'hir>(mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> { while let ExprKind::Block(block, _) = expr.kind { match (block.stmts, block.expr) { @@ -122,13 +169,13 @@ fn strip_singleton_blocks<'hir>(mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> } /// A "wild-like" pattern is wild ("_") or `None`. -/// For this lint to apply, both the outer and inner match expressions +/// For this lint to apply, both the outer and inner patterns /// must have "wild-like" branches that can be combined. -fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { - if arm.guard.is_some() { +fn is_wild_like(cx: &LateContext<'_>, pat_kind: &PatKind<'_>, arm_guard: &Option>) -> bool { + if arm_guard.is_some() { return false; } - match arm.pat.kind { + match pat_kind { PatKind::Binding(..) | PatKind::Wild => true, PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), _ => false, diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 2dcd55457993c..5eb99cfe24f49 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -316,9 +316,10 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> Option< let mut start_eq = usize::MAX; let mut end_eq = usize::MAX; let mut expr_eq = true; - for win in blocks.windows(2) { - let l_stmts = win[0].stmts; - let r_stmts = win[1].stmts; + let mut iter = blocks.windows(2); + while let Some(&[win0, win1]) = iter.next() { + let l_stmts = win0.stmts; + let r_stmts = win1.stmts; // `SpanlessEq` now keeps track of the locals and is therefore context sensitive clippy#6752. // The comparison therefore needs to be done in a way that builds the correct context. @@ -335,22 +336,22 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> Option< it1.zip(it2) .fold(0, |acc, (l, r)| if evaluator.eq_stmt(l, r) { acc + 1 } else { 0 }) }; - let block_expr_eq = both(&win[0].expr, &win[1].expr, |l, r| evaluator.eq_expr(l, r)); + let block_expr_eq = both(&win0.expr, &win1.expr, |l, r| evaluator.eq_expr(l, r)); // IF_SAME_THEN_ELSE if_chain! { if block_expr_eq; if l_stmts.len() == r_stmts.len(); if l_stmts.len() == current_start_eq; - if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, win[0].hir_id); - if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, win[1].hir_id); + if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, win0.hir_id); + if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, win1.hir_id); then { span_lint_and_note( cx, IF_SAME_THEN_ELSE, - win[0].span, + win0.span, "this `if` has identical blocks", - Some(win[1].span), + Some(win1.span), "same as this", ); diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index ded7001ad8c86..7825e5f6ed52e 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -232,6 +232,7 @@ fn is_linted_explicit_deref_position(parent: Option>, child_id: HirId, | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..) + | ExprKind::Let(..) | ExprKind::Closure(..) | ExprKind::Block(..) | ExprKind::Assign(..) diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index e1d0d65edb1b9..627f746ec9971 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -1,3 +1,4 @@ +use clippy_utils::higher; use clippy_utils::{ can_move_expr_to_closure_no_visit, diagnostics::span_lint_and_sugg, @@ -5,6 +6,7 @@ use clippy_utils::{ source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context}, SpanlessEq, }; +use core::fmt::Write; use rustc_errors::Applicability; use rustc_hir::{ intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}, @@ -13,7 +15,6 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{Span, SyntaxContext, DUMMY_SP}; -use std::fmt::Write; declare_clippy_lint! { /// ### What it does @@ -62,10 +63,11 @@ declare_lint_pass!(HashMapPass => [MAP_ENTRY]); impl<'tcx> LateLintPass<'tcx> for HashMapPass { #[allow(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let (cond_expr, then_expr, else_expr) = match expr.kind { - ExprKind::If(c, t, e) => (c, t, e), + let (cond_expr, then_expr, else_expr) = match higher::If::hir(expr) { + Some(higher::If { cond, then, r#else }) => (cond, then, r#else), _ => return, }; + let (map_ty, contains_expr) = match try_parse_contains(cx, cond_expr) { Some(x) => x, None => return, diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 192b69e18f90f..f6a64a8ca6031 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -100,7 +100,7 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if ex.span.ctxt() != expr.span.ctxt() { if decl.inputs.is_empty() { - if let Some(VecArgs::Vec(&[])) = higher::vec_macro(cx, ex) { + if let Some(VecArgs::Vec(&[])) = higher::VecArgs::hir(cx, ex) { // replace `|| vec![]` with `Vec::new` span_lint_and_sugg( cx, diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index b01c0cdd84624..d12482e7b7bb9 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -3,6 +3,7 @@ use clippy_utils::consts::{ Constant::{Int, F32, F64}, }; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::higher; use clippy_utils::{eq_expr_value, get_parent_expr, numeric_literal, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -545,11 +546,11 @@ fn are_negated<'a>(cx: &LateContext<'_>, expr1: &'a Expr<'a>, expr2: &'a Expr<'a fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let ExprKind::If(cond, body, else_body) = expr.kind; - if let ExprKind::Block(block, _) = body.kind; + if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr); + if let ExprKind::Block(block, _) = then.kind; if block.stmts.is_empty(); if let Some(if_body_expr) = block.expr; - if let Some(ExprKind::Block(else_block, _)) = else_body.map(|el| &el.kind); + if let Some(ExprKind::Block(else_block, _)) = r#else.map(|el| &el.kind); if else_block.stmts.is_empty(); if let Some(else_body_expr) = else_block.expr; if let Some((if_expr_positive, body)) = are_negated(cx, if_body_expr, else_body_expr); diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs index d3ddeda9fd1b9..e2d3905eacb50 100644 --- a/clippy_lints/src/if_let_mutex.rs +++ b/clippy_lints/src/if_let_mutex.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::higher; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::SpanlessEq; use if_chain::if_chain; use rustc_hir::intravisit::{self as visit, NestedVisitorMap, Visitor}; -use rustc_hir::{Expr, ExprKind, MatchSource}; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -42,7 +43,7 @@ declare_clippy_lint! { declare_lint_pass!(IfLetMutex => [IF_LET_MUTEX]); impl<'tcx> LateLintPass<'tcx> for IfLetMutex { - fn check_expr(&mut self, cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>) { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { let mut arm_visit = ArmVisitor { mutex_lock_called: false, found_mutex: None, @@ -53,25 +54,23 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex { found_mutex: None, cx, }; - if let ExprKind::Match( - op, - arms, - MatchSource::IfLetDesugar { - contains_else_clause: true, - }, - ) = ex.kind + if let Some(higher::IfLet { + let_expr, + if_then, + if_else: Some(if_else), + .. + }) = higher::IfLet::hir(expr) { - op_visit.visit_expr(op); + op_visit.visit_expr(let_expr); if op_visit.mutex_lock_called { - for arm in arms { - arm_visit.visit_arm(arm); - } + arm_visit.visit_expr(if_then); + arm_visit.visit_expr(if_else); if arm_visit.mutex_lock_called && arm_visit.same_mutex(cx, op_visit.found_mutex.unwrap()) { span_lint_and_help( cx, IF_LET_MUTEX, - ex.span, + expr.span, "calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock", None, "move the lock call outside of the `if let ...` expression", diff --git a/clippy_lints/src/if_let_some_result.rs b/clippy_lints/src/if_let_some_result.rs index 587307811a113..cd813c639dbbf 100644 --- a/clippy_lints/src/if_let_some_result.rs +++ b/clippy_lints/src/if_let_some_result.rs @@ -1,10 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::higher; use clippy_utils::method_chain_args; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, MatchSource, PatKind, QPath}; +use rustc_hir::{Expr, ExprKind, PatKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -44,17 +45,17 @@ declare_lint_pass!(OkIfLet => [IF_LET_SOME_RESULT]); impl<'tcx> LateLintPass<'tcx> for OkIfLet { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { //begin checking variables - if let ExprKind::Match(op, body, MatchSource::IfLetDesugar { .. }) = expr.kind; //test if expr is if let - if let ExprKind::MethodCall(_, ok_span, result_types, _) = op.kind; //check is expr.ok() has type Result.ok(, _) - if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _) = body[0].pat.kind; //get operation - if method_chain_args(op, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized; + if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(expr); + if let ExprKind::MethodCall(_, ok_span, ref result_types, _) = let_expr.kind; //check is expr.ok() has type Result.ok(, _) + if let PatKind::TupleStruct(QPath::Resolved(_, ref x), ref y, _) = let_pat.kind; //get operation + if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized; if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&result_types[0]), sym::result_type); if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some"; then { let mut applicability = Applicability::MachineApplicable; let some_expr_string = snippet_with_applicability(cx, y[0].span, "", &mut applicability); - let trimmed_ok = snippet_with_applicability(cx, op.span.until(ok_span), "", &mut applicability); + let trimmed_ok = snippet_with_applicability(cx, let_expr.span.until(ok_span), "", &mut applicability); let sugg = format!( "if let Ok({}) = {}", some_expr_string, @@ -63,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for OkIfLet { span_lint_and_sugg( cx, IF_LET_SOME_RESULT, - expr.span.with_hi(op.span.hi()), + expr.span.with_hi(let_expr.span.hi()), "matching on `Some` with `ok()` is redundant", &format!("consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string), sugg, diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 17b9a2f888e0d..a2dac57454f2d 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet_with_macro_callsite; -use clippy_utils::{is_else_clause, is_lang_ctor, meets_msrv, msrvs}; +use clippy_utils::{higher, is_else_clause, is_lang_ctor, meets_msrv, msrvs}; use if_chain::if_chain; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind}; @@ -70,7 +70,7 @@ impl LateLintPass<'_> for IfThenSomeElseNone { } if_chain! { - if let ExprKind::If(cond, then, Some(els)) = expr.kind; + if let Some(higher::If { cond, then, r#else: Some(els) }) = higher::If::hir(expr); if let ExprKind::Block(then_block, _) = then.kind; if let Some(then_expr) = then_block.expr; if let ExprKind::Call(then_call, [then_arg]) = then_expr.kind; diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 0a7d31dce2fd5..79d4d7ddcbced 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::higher; use clippy_utils::{in_macro, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast::LitKind; @@ -42,7 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { return; } if_chain! { - if let ExprKind::If(cond, then, None) = &expr.kind; + if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr); // Check if the conditional expression is a binary operation if let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind; diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index 8c1f107330955..f52f090d3872e 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Index(array, index) = &expr.kind { let ty = cx.typeck_results().expr_ty(array).peel_refs(); - if let Some(range) = higher::range(index) { + if let Some(range) = higher::Range::hir(index) { // Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..] if let ty::Array(_, s) = ty.kind() { let size: u128 = if let Some(size) = s.try_eval_usize(cx.tcx, cx.param_env) { diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index 2411a3175b919..58646385def52 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -172,7 +172,7 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { Finite } }, - ExprKind::Struct(..) => higher::range(expr).map_or(false, |r| r.end.is_none()).into(), + ExprKind::Struct(..) => higher::Range::hir(expr).map_or(false, |r| r.end.is_none()).into(), _ => Finite, } } diff --git a/clippy_lints/src/let_if_seq.rs b/clippy_lints/src/let_if_seq.rs index 13f0d43cf8dd1..0594b73dd3837 100644 --- a/clippy_lints/src/let_if_seq.rs +++ b/clippy_lints/src/let_if_seq.rs @@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq { if let hir::StmtKind::Local(local) = stmt.kind; if let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind; if let hir::StmtKind::Expr(if_) = expr.kind; - if let hir::ExprKind::If(cond, then, ref else_) = if_.kind; + if let hir::ExprKind::If(hir::Expr { kind: hir::ExprKind::DropTemps(cond), ..}, then, else_) = if_.kind; let mut used_visitor = LocalUsedVisitor::new(cx, canonical_id); if !used_visitor.check_expr(cond); if let hir::ExprKind::Block(then, _) = then.kind; @@ -79,7 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq { ); if has_interior_mutability { return; } - let (default_multi_stmts, default) = if let Some(else_) = *else_ { + let (default_multi_stmts, default) = if let Some(else_) = else_ { if let hir::ExprKind::Block(else_, _) = else_.kind { if let Some(default) = check_assign(cx, canonical_id, else_) { (else_.stmts.len() > 1, default) diff --git a/clippy_lints/src/loops/manual_flatten.rs b/clippy_lints/src/loops/manual_flatten.rs index 64ff7574f86b7..9f2bc3c7ebae7 100644 --- a/clippy_lints/src/loops/manual_flatten.rs +++ b/clippy_lints/src/loops/manual_flatten.rs @@ -1,11 +1,12 @@ use super::utils::make_iterator_snippet; use super::MANUAL_FLATTEN; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::higher; use clippy_utils::{is_lang_ctor, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionSome, ResultOk}; -use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, StmtKind}; +use rustc_hir::{Expr, ExprKind, Pat, PatKind, StmtKind}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::source_map::Span; @@ -36,14 +37,12 @@ pub(super) fn check<'tcx>( if_chain! { if let Some(inner_expr) = inner_expr; - if let ExprKind::Match( - match_expr, match_arms, MatchSource::IfLetDesugar{ contains_else_clause: false } - ) = inner_expr.kind; + if let Some(higher::IfLet { let_pat, let_expr, if_else: None, .. }) = higher::IfLet::hir(inner_expr); // Ensure match_expr in `if let` statement is the same as the pat from the for-loop if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind; - if path_to_local_id(match_expr, pat_hir_id); + if path_to_local_id(let_expr, pat_hir_id); // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result` - if let PatKind::TupleStruct(ref qpath, _, _) = match_arms[0].pat.kind; + if let PatKind::TupleStruct(ref qpath, _, _) = let_pat.kind; let some_ctor = is_lang_ctor(cx, qpath, OptionSome); let ok_ctor = is_lang_ctor(cx, qpath, ResultOk); if some_ctor || ok_ctor; @@ -55,7 +54,7 @@ pub(super) fn check<'tcx>( // Prepare the help message let mut applicability = Applicability::MaybeIncorrect; let arg_snippet = make_iterator_snippet(cx, arg, &mut applicability); - let copied = match cx.typeck_results().expr_ty(match_expr).kind() { + let copied = match cx.typeck_results().expr_ty(let_expr).kind() { ty::Ref(_, inner, _) => match inner.kind() { ty::Ref(..) => ".copied()", _ => "" diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs index a98e2dc1372db..2525b14e1c5c3 100644 --- a/clippy_lints/src/loops/manual_memcpy.rs +++ b/clippy_lints/src/loops/manual_memcpy.rs @@ -27,7 +27,7 @@ pub(super) fn check<'tcx>( start: Some(start), end: Some(end), limits, - }) = higher::range(arg) + }) = higher::Range::hir(arg) { // the var must be a single name if let PatKind::Binding(_, canonical_id, _, _) = pat.kind { diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 7ca54d5397205..bd9de5e08d736 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -551,7 +551,7 @@ declare_lint_pass!(Loops => [ impl<'tcx> LateLintPass<'tcx> for Loops { #[allow(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let Some((pat, arg, body, span)) = higher::for_loop(expr) { + if let Some(higher::ForLoop { pat, arg, body, span }) = higher::ForLoop::hir(expr) { // we don't want to check expanded macros // this check is not at the top of the function // since higher::for_loop expressions are marked as expansions @@ -580,8 +580,8 @@ impl<'tcx> LateLintPass<'tcx> for Loops { while_let_on_iterator::check(cx, expr); - if let Some((cond, body)) = higher::while_loop(expr) { - while_immutable_condition::check(cx, cond, body); + if let Some(higher::While { if_cond, if_then, .. }) = higher::While::hir(&expr) { + while_immutable_condition::check(cx, if_cond, if_then); } needless_collect::check(expr, cx); diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index 1e54a1e2de165..344dc5074d369 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -14,7 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) { start: Some(start), end: Some(end), .. - }) = higher::range(arg) + }) = higher::Range::hir(arg) { let mut_ids = vec![check_for_mutability(cx, start), check_for_mutability(cx, end)]; if mut_ids[0].is_some() || mut_ids[1].is_some() { diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 3810d0dcc051a..3f77e7af927ad 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -34,7 +34,7 @@ pub(super) fn check<'tcx>( start: Some(start), ref end, limits, - }) = higher::range(arg) + }) = higher::Range::hir(arg) { // the var must be a single name if let PatKind::Binding(_, canonical_id, ident, _) = pat.kind { diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 6d9f6215ed418..2c46971d5f741 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -1,7 +1,7 @@ use super::utils::make_iterator_snippet; use super::NEVER_LOOP; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::higher; +use clippy_utils::higher::ForLoop; use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, HirId, InlineAsmOperand, LoopSource, Node, Pat, Stmt, StmtKind}; @@ -16,7 +16,7 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { if let LoopSource::ForLoop = source; if let Some((_, Node::Expr(parent_match))) = cx.tcx.hir().parent_iter(expr.hir_id).nth(1); - if let Some((pat, iterator, _, for_span)) = higher::for_loop(parent_match); + if let Some(ForLoop { arg: iterator, pat, span: for_span, .. }) = ForLoop::hir(parent_match); then { // Suggests using an `if let` instead. This is `Unspecified` because the // loop may (probably) contain `break` statements which would be invalid @@ -111,6 +111,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { | ExprKind::Unary(_, e) | ExprKind::Cast(e, _) | ExprKind::Type(e, _) + | ExprKind::Let(_, e, _) | ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) | ExprKind::Struct(_, _, Some(e)) @@ -128,7 +129,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { // Break can come from the inner loop so remove them. absorb_break(&never_loop_block(b, main_loop_id)) }, - ExprKind::If(e, e2, ref e3) => { + ExprKind::If(e, e2, e3) => { let e1 = never_loop_expr(e, main_loop_id); let e2 = never_loop_expr(e2, main_loop_id); let e3 = e3 @@ -156,7 +157,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { NeverLoopResult::AlwaysBreak } }, - ExprKind::Break(_, ref e) | ExprKind::Ret(ref e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| { + ExprKind::Break(_, e) | ExprKind::Ret(e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| { combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak) }), ExprKind::InlineAsm(asm) => asm diff --git a/clippy_lints/src/loops/while_let_loop.rs b/clippy_lints/src/loops/while_let_loop.rs index 9c1720798529d..6be410ca8e3cb 100644 --- a/clippy_lints/src/loops/while_let_loop.rs +++ b/clippy_lints/src/loops/while_let_loop.rs @@ -1,8 +1,9 @@ use super::WHILE_LET_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::higher; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; -use rustc_hir::{Block, Expr, ExprKind, MatchSource, StmtKind}; +use rustc_hir::{Block, Expr, ExprKind, MatchSource, Pat, StmtKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; @@ -11,41 +12,25 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &' let inner_stmt_expr = extract_expr_from_first_stmt(loop_block); // or extract the first expression (if any) from the block if let Some(inner) = inner_stmt_expr.or_else(|| extract_first_expr(loop_block)) { - if let ExprKind::Match(matchexpr, arms, ref source) = inner.kind { - // ensure "if let" compatible match structure - match *source { - MatchSource::Normal | MatchSource::IfLetDesugar { .. } => { - if arms.len() == 2 - && arms[0].guard.is_none() - && arms[1].guard.is_none() - && is_simple_break_expr(arms[1].body) - { - if in_external_macro(cx.sess(), expr.span) { - return; - } + if let Some(higher::IfLet { + let_pat, + let_expr, + if_else: Some(if_else), + .. + }) = higher::IfLet::hir(inner) + { + if is_simple_break_expr(if_else) { + could_be_while_let(cx, expr, let_pat, let_expr); + } + } - // NOTE: we used to build a body here instead of using - // ellipsis, this was removed because: - // 1) it was ugly with big bodies; - // 2) it was not indented properly; - // 3) it wasn’t very smart (see #675). - let mut applicability = Applicability::HasPlaceholders; - span_lint_and_sugg( - cx, - WHILE_LET_LOOP, - expr.span, - "this loop could be written as a `while let` loop", - "try", - format!( - "while let {} = {} {{ .. }}", - snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability), - snippet_with_applicability(cx, matchexpr.span, "..", &mut applicability), - ), - applicability, - ); - } - }, - _ => (), + if let ExprKind::Match(ref matchexpr, ref arms, MatchSource::Normal) = inner.kind { + if arms.len() == 2 + && arms[0].guard.is_none() + && arms[1].guard.is_none() + && is_simple_break_expr(&arms[1].body) + { + could_be_while_let(cx, expr, &arms[0].pat, matchexpr); } } } @@ -54,14 +39,12 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &' /// If a block begins with a statement (possibly a `let` binding) and has an /// expression, return it. fn extract_expr_from_first_stmt<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> { - if block.stmts.is_empty() { - return None; - } - if let StmtKind::Local(local) = block.stmts[0].kind { - local.init //.map(|expr| expr) - } else { - None + if let Some(first_stmt) = block.stmts.get(0) { + if let StmtKind::Local(local) = first_stmt.kind { + return local.init; + } } + None } /// If a block begins with an expression (with or without semicolon), return it. @@ -86,3 +69,34 @@ fn is_simple_break_expr(expr: &Expr<'_>) -> bool { _ => false, } } + +fn could_be_while_let<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + let_pat: &'tcx Pat<'_>, + let_expr: &'tcx Expr<'_>, +) { + if in_external_macro(cx.sess(), expr.span) { + return; + } + + // NOTE: we used to build a body here instead of using + // ellipsis, this was removed because: + // 1) it was ugly with big bodies; + // 2) it was not indented properly; + // 3) it wasn’t very smart (see #675). + let mut applicability = Applicability::HasPlaceholders; + span_lint_and_sugg( + cx, + WHILE_LET_LOOP, + expr.span, + "this loop could be written as a `while let` loop", + "try", + format!( + "while let {} = {} {{ .. }}", + snippet_with_applicability(cx, let_pat.span, "..", &mut applicability), + snippet_with_applicability(cx, let_expr.span, "..", &mut applicability), + ), + applicability, + ); +} diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index ef822e0cbe540..0757d329125cb 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -1,5 +1,6 @@ use super::WHILE_LET_ON_ITERATOR; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::higher; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{ get_enclosing_loop_or_closure, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used, @@ -7,27 +8,31 @@ use clippy_utils::{ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}; -use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, MatchSource, Mutability, Node, PatKind, QPath, UnOp}; +use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_span::{symbol::sym, Span, Symbol}; pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let (scrutinee_expr, iter_expr, some_pat, loop_expr) = if_chain! { - if let ExprKind::Match(scrutinee_expr, [arm, _], MatchSource::WhileLetDesugar) = expr.kind; + if let Some(higher::WhileLet { + if_then, + let_pat, + let_expr, + .. + }) = higher::WhileLet::hir(expr); // check for `Some(..)` pattern - if let PatKind::TupleStruct(QPath::Resolved(None, pat_path), some_pat, _) = arm.pat.kind; + if let PatKind::TupleStruct(QPath::Resolved(None, pat_path), some_pat, _) = let_pat.kind; if let Res::Def(_, pat_did) = pat_path.res; if match_def_path(cx, pat_did, &paths::OPTION_SOME); // check for call to `Iterator::next` - if let ExprKind::MethodCall(method_name, _, [iter_expr], _) = scrutinee_expr.kind; + if let ExprKind::MethodCall(method_name, _, [iter_expr], _) = let_expr.kind; if method_name.ident.name == sym::next; - if is_trait_method(cx, scrutinee_expr, sym::Iterator); - if let Some(iter_expr) = try_parse_iter_expr(cx, iter_expr); + if is_trait_method(cx, let_expr, sym::Iterator); + if let Some(iter_expr_struct) = try_parse_iter_expr(cx, iter_expr); // get the loop containing the match expression - if let Some((_, Node::Expr(loop_expr))) = cx.tcx.hir().parent_iter(expr.hir_id).nth(1); - if !uses_iter(cx, &iter_expr, arm.body); + if !uses_iter(cx, &iter_expr_struct, if_then); then { - (scrutinee_expr, iter_expr, some_pat, loop_expr) + (let_expr, iter_expr_struct, some_pat, expr) } else { return; } @@ -81,6 +86,7 @@ struct IterExpr { /// The path being used. path: Res, } + /// Parses any expression to find out which field of which variable is used. Will return `None` if /// the expression might have side effects. fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option { @@ -285,6 +291,7 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr: } impl Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> { type Map = ErasedMap<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index 7dec1595e0d10..53d97f775435a 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -1,5 +1,6 @@ use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::higher; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; use clippy_utils::{ @@ -9,7 +10,7 @@ use clippy_utils::{ use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; -use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, HirId, MatchSource, Mutability, Pat, PatKind}; +use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, HirId, Mutability, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -43,155 +44,168 @@ declare_lint_pass!(ManualMap => [MANUAL_MAP]); impl LateLintPass<'_> for ManualMap { #[allow(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Match( - scrutinee, - [arm1 @ Arm { guard: None, .. }, arm2 @ Arm { guard: None, .. }], - match_kind, - ) = expr.kind + if let Some(higher::IfLet { + let_pat, + let_expr, + if_then, + if_else: Some(if_else), + }) = higher::IfLet::hir(expr) { - if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) { - return; - } + manage_lint(cx, expr, (&let_pat.kind, if_then), (&PatKind::Wild, if_else), let_expr); + } - let (scrutinee_ty, ty_ref_count, ty_mutability) = - peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee)); - if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::option_type) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::option_type)) - { - return; - } + if let ExprKind::Match(scrutinee, [then @ Arm { guard: None, .. }, r#else @ Arm { guard: None, .. }], _) = + expr.kind + { + manage_lint( + cx, + expr, + (&then.pat.kind, then.body), + (&r#else.pat.kind, r#else.body), + scrutinee, + ); + } + } +} - let expr_ctxt = expr.span.ctxt(); - let (some_expr, some_pat, pat_ref_count, is_wild_none) = match ( - try_parse_pattern(cx, arm1.pat, expr_ctxt), - try_parse_pattern(cx, arm2.pat, expr_ctxt), - ) { - (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) - if is_none_expr(cx, arm1.body) => - { - (arm2.body, pattern, ref_count, true) - }, - (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) - if is_none_expr(cx, arm1.body) => - { - (arm2.body, pattern, ref_count, false) - }, - (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) - if is_none_expr(cx, arm2.body) => - { - (arm1.body, pattern, ref_count, true) - }, - (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) - if is_none_expr(cx, arm2.body) => - { - (arm1.body, pattern, ref_count, false) - }, - _ => return, - }; +fn manage_lint<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + then: (&'tcx PatKind<'_>, &'tcx Expr<'_>), + r#else: (&'tcx PatKind<'_>, &'tcx Expr<'_>), + scrut: &'tcx Expr<'_>, +) { + if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) { + return; + } - // Top level or patterns aren't allowed in closures. - if matches!(some_pat.kind, PatKind::Or(_)) { - return; - } + let (scrutinee_ty, ty_ref_count, ty_mutability) = peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrut)); + if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::option_type) + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::option_type)) + { + return; + } - let some_expr = match get_some_expr(cx, some_expr, expr_ctxt) { - Some(expr) => expr, - None => return, - }; + let (then_pat, then_expr) = then; + let (else_pat, else_expr) = r#else; - // These two lints will go back and forth with each other. - if cx.typeck_results().expr_ty(some_expr) == cx.tcx.types.unit - && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id) - { - return; - } + let expr_ctxt = expr.span.ctxt(); + let (some_expr, some_pat, pat_ref_count, is_wild_none) = match ( + try_parse_pattern(cx, then_pat, expr_ctxt), + try_parse_pattern(cx, else_pat, expr_ctxt), + ) { + (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_expr) => { + (else_expr, pattern, ref_count, true) + }, + (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_expr) => { + (else_expr, pattern, ref_count, false) + }, + (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_expr) => { + (then_expr, pattern, ref_count, true) + }, + (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_expr) => { + (then_expr, pattern, ref_count, false) + }, + _ => return, + }; - // `map` won't perform any adjustments. - if !cx.typeck_results().expr_adjustments(some_expr).is_empty() { - return; - } + // Top level or patterns aren't allowed in closures. + if matches!(some_pat.kind, PatKind::Or(_)) { + return; + } - if !can_move_expr_to_closure(cx, some_expr) { - return; - } + let some_expr = match get_some_expr(cx, some_expr, expr_ctxt) { + Some(expr) => expr, + None => return, + }; - // Determine which binding mode to use. - let explicit_ref = some_pat.contains_explicit_ref_binding(); - let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then(|| ty_mutability)); + if cx.typeck_results().expr_ty(some_expr) == cx.tcx.types.unit && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id) { + return; + } - let as_ref_str = match binding_ref { - Some(Mutability::Mut) => ".as_mut()", - Some(Mutability::Not) => ".as_ref()", - None => "", - }; + // `map` won't perform any adjustments. + if !cx.typeck_results().expr_adjustments(some_expr).is_empty() { + return; + } - let mut app = Applicability::MachineApplicable; + if !can_move_expr_to_closure(cx, some_expr) { + return; + } - // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or - // it's being passed by value. - let scrutinee = peel_hir_expr_refs(scrutinee).0; - let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); - let scrutinee_str = - if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX { - format!("({})", scrutinee_str) - } else { - scrutinee_str.into() - }; + // Determine which binding mode to use. + let explicit_ref = some_pat.contains_explicit_ref_binding(); + let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then(|| ty_mutability)); + + let as_ref_str = match binding_ref { + Some(Mutability::Mut) => ".as_mut()", + Some(Mutability::Not) => ".as_ref()", + None => "", + }; + + let mut app = Applicability::MachineApplicable; - let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind { - match can_pass_as_func(cx, id, some_expr) { - Some(func) if func.span.ctxt() == some_expr.span.ctxt() => { - snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() - }, - _ => { - if path_to_local_id(some_expr, id) - && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id) - && binding_ref.is_some() - { - return; - } + // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or + // it's being passed by value. + let scrutinee = peel_hir_expr_refs(scrut).0; + let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); + let scrutinee_str = if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX { + format!("({})", scrutinee_str) + } else { + scrutinee_str.into() + }; - // `ref` and `ref mut` annotations were handled earlier. - let annotation = if matches!(annotation, BindingAnnotation::Mutable) { - "mut " - } else { - "" - }; - format!( - "|{}{}| {}", - annotation, - some_binding, - snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0 - ) - }, + let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind { + match can_pass_as_func(cx, id, some_expr) { + Some(func) if func.span.ctxt() == some_expr.span.ctxt() => { + snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() + }, + _ => { + if path_to_local_id(some_expr, id) + && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id) + && binding_ref.is_some() + { + return; } - } else if !is_wild_none && explicit_ref.is_none() { - // TODO: handle explicit reference annotations. + + // `ref` and `ref mut` annotations were handled earlier. + let annotation = if matches!(annotation, BindingAnnotation::Mutable) { + "mut " + } else { + "" + }; format!( - "|{}| {}", - snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0, + "|{}{}| {}", + annotation, + some_binding, snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0 ) - } else { - // Refutable bindings and mixed reference annotations can't be handled by `map`. - return; - }; - - span_lint_and_sugg( - cx, - MANUAL_MAP, - expr.span, - "manual implementation of `Option::map`", - "try this", - if matches!(match_kind, MatchSource::IfLetDesugar { .. }) && is_else_clause(cx.tcx, expr) { - format!("{{ {}{}.map({}) }}", scrutinee_str, as_ref_str, body_str) - } else { - format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str) - }, - app, - ); + }, } - } + } else if !is_wild_none && explicit_ref.is_none() { + // TODO: handle explicit reference annotations. + format!( + "|{}| {}", + snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0, + snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0 + ) + } else { + // Refutable bindings and mixed reference annotations can't be handled by `map`. + return; + }; + + span_lint_and_sugg( + cx, + MANUAL_MAP, + expr.span, + "manual implementation of `Option::map`", + "try this", + if is_else_clause(cx.tcx, expr) { + format!("{{ {}{}.map({}) }}", scrutinee_str, as_ref_str, body_str) + } else { + format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str) + }, + app, + ); } // Checks whether the expression could be passed as a function, or whether a closure is needed. @@ -199,7 +213,7 @@ impl LateLintPass<'_> for ManualMap { fn can_pass_as_func(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { match expr.kind { ExprKind::Call(func, [arg]) - if path_to_local_id(arg, binding) && cx.typeck_results().expr_adjustments(arg).is_empty() => + if path_to_local_id (arg, binding) && cx.typeck_results().expr_adjustments(arg).is_empty() => { Some(func) }, @@ -221,21 +235,28 @@ enum OptionPat<'a> { // Try to parse into a recognized `Option` pattern. // i.e. `_`, `None`, `Some(..)`, or a reference to any of those. -fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option> { - fn f(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ref_count: usize, ctxt: SyntaxContext) -> Option> { - match pat.kind { +fn try_parse_pattern( + cx: &LateContext<'tcx>, + pat_kind: &'tcx PatKind<'_>, + ctxt: SyntaxContext, +) -> Option> { + fn f( + cx: &LateContext<'tcx>, + pat_kind: &'tcx PatKind<'_>, + ref_count: usize, + ctxt: SyntaxContext, + ) -> Option> { + match pat_kind { PatKind::Wild => Some(OptionPat::Wild), - PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), + PatKind::Ref(ref_pat, _) => f(cx, &ref_pat.kind, ref_count + 1, ctxt), PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone) => Some(OptionPat::None), - PatKind::TupleStruct(ref qpath, [pattern], _) - if is_lang_ctor(cx, qpath, OptionSome) && pat.span.ctxt() == ctxt => - { + PatKind::TupleStruct(ref qpath, [pattern], _) if is_lang_ctor(cx, qpath, OptionSome) => { Some(OptionPat::Some { pattern, ref_count }) }, _ => None, } } - f(cx, pat, 0, ctxt) + f(cx, pat_kind, 0, ctxt) } // Checks for an expression wrapped by the `Some` constructor. Returns the contained expression. diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index db12c377488b0..4e040508b6bfb 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -73,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { } if_chain! { - if let ExprKind::If(cond, then, _) = &expr.kind; + if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr); if let ExprKind::MethodCall(_, _, [target_arg, pattern], _) = cond.kind; if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id); if let ExprKind::Path(target_path) = &target_arg.kind; @@ -212,7 +212,7 @@ fn find_stripping<'tcx>( if is_ref_str(self.cx, ex); let unref = peel_ref(ex); if let ExprKind::Index(indexed, index) = &unref.kind; - if let Some(higher::Range { start, end, .. }) = higher::range(index); + if let Some(higher::Range { start, end, .. }) = higher::Range::hir(index); if let ExprKind::Path(path) = &indexed.kind; if self.cx.qpath_res(path, ex.hir_id) == self.target; then { diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 5360c02f90539..a183d0c66e8ce 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -2,6 +2,7 @@ use clippy_utils::consts::{constant, miri_to_const, Constant}; use clippy_utils::diagnostics::{ multispan_sugg, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, }; +use clippy_utils::higher; use clippy_utils::source::{expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs}; @@ -12,8 +13,10 @@ use clippy_utils::{ strip_pat_refs, }; use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash}; +use core::array; +use core::iter::{once, ExactSizeIterator}; use if_chain::if_chain; -use rustc_ast::ast::LitKind; +use rustc_ast::ast::{Attribute, LitKind}; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -628,8 +631,11 @@ impl<'tcx> LateLintPass<'tcx> for Matches { check_match_single_binding(cx, ex, arms, expr); } } - if let ExprKind::Match(ex, arms, _) = expr.kind { - check_match_ref_pats(cx, ex, arms, expr); + if let ExprKind::Match(ref ex, ref arms, _) = expr.kind { + check_match_ref_pats(cx, ex, arms.iter().map(|el| el.pat), expr); + } + if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(expr) { + check_match_ref_pats(cx, let_expr, once(let_pat), expr); } } @@ -1179,39 +1185,40 @@ fn is_panic_block(block: &Block<'_>) -> bool { } } -fn check_match_ref_pats(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { - if has_only_ref_pats(arms) { - let mut suggs = Vec::with_capacity(arms.len() + 1); - let (title, msg) = if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = ex.kind { - let span = ex.span.source_callsite(); - suggs.push((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string())); - ( - "you don't need to add `&` to both the expression and the patterns", - "try", - ) - } else { - let span = ex.span.source_callsite(); - suggs.push((span, Sugg::hir_with_macro_callsite(cx, ex, "..").deref().to_string())); - ( - "you don't need to add `&` to all patterns", - "instead of prefixing all patterns with `&`, you can dereference the expression", - ) - }; - - suggs.extend(arms.iter().filter_map(|a| { - if let PatKind::Ref(refp, _) = a.pat.kind { - Some((a.pat.span, snippet(cx, refp.span, "..").to_string())) - } else { - None - } - })); +fn check_match_ref_pats<'a, 'b, I>(cx: &LateContext<'_>, ex: &Expr<'_>, pats: I, expr: &Expr<'_>) +where + 'b: 'a, + I: Clone + Iterator>, +{ + if !has_only_ref_pats(pats.clone()) { + return; + } - span_lint_and_then(cx, MATCH_REF_PATS, expr.span, title, |diag| { - if !expr.span.from_expansion() { - multispan_sugg(diag, msg, suggs); - } - }); + let (first_sugg, msg, title); + let span = ex.span.source_callsite(); + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = ex.kind { + first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string())); + msg = "try"; + title = "you don't need to add `&` to both the expression and the patterns"; + } else { + first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, ex, "..").deref().to_string())); + msg = "instead of prefixing all patterns with `&`, you can dereference the expression"; + title = "you don't need to add `&` to all patterns"; } + + let remaining_suggs = pats.filter_map(|pat| { + if let PatKind::Ref(ref refp, _) = pat.kind { + Some((pat.span, snippet(cx, refp.span, "..").to_string())) + } else { + None + } + }); + + span_lint_and_then(cx, MATCH_REF_PATS, expr.span, title, |diag| { + if !expr.span.from_expansion() { + multispan_sugg(diag, msg, first_sugg.chain(remaining_suggs)); + } + }); } fn check_match_as_ref(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { @@ -1286,46 +1293,99 @@ fn check_wild_in_or_pats(cx: &LateContext<'_>, arms: &[Arm<'_>]) { /// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!` fn check_match_like_matches<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { - if let ExprKind::Match(ex, arms, ref match_source) = &expr.kind { - match match_source { - MatchSource::Normal => find_matches_sugg(cx, ex, arms, expr, false), - MatchSource::IfLetDesugar { .. } => find_matches_sugg(cx, ex, arms, expr, true), - _ => false, - } - } else { - false + if let Some(higher::IfLet { + let_pat, + let_expr, + if_then, + if_else: Some(if_else), + }) = higher::IfLet::hir(expr) + { + return find_matches_sugg( + cx, + let_expr, + array::IntoIter::new([(&[][..], Some(let_pat), if_then, None), (&[][..], None, if_else, None)]), + expr, + true, + ); } + + if let ExprKind::Match(scrut, arms, MatchSource::Normal) = expr.kind { + return find_matches_sugg( + cx, + scrut, + arms.iter().map(|arm| { + ( + cx.tcx.hir().attrs(arm.hir_id), + Some(arm.pat), + arm.body, + arm.guard.as_ref(), + ) + }), + expr, + false, + ); + } + + false } -/// Lint a `match` or desugared `if let` for replacement by `matches!` -fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>, desugared: bool) -> bool { +/// Lint a `match` or `if let` for replacement by `matches!` +fn find_matches_sugg<'a, 'b, I>( + cx: &LateContext<'_>, + ex: &Expr<'_>, + mut iter: I, + expr: &Expr<'_>, + is_if_let: bool, +) -> bool +where + 'b: 'a, + I: Clone + + DoubleEndedIterator + + ExactSizeIterator + + Iterator< + Item = ( + &'a [Attribute], + Option<&'a Pat<'b>>, + &'a Expr<'b>, + Option<&'a Guard<'b>>, + ), + >, +{ if_chain! { - if arms.len() >= 2; + if iter.len() >= 2; if cx.typeck_results().expr_ty(expr).is_bool(); - if let Some((b1_arm, b0_arms)) = arms.split_last(); - if let Some(b0) = find_bool_lit(&b0_arms[0].body.kind, desugared); - if let Some(b1) = find_bool_lit(&b1_arm.body.kind, desugared); - if is_wild(b1_arm.pat); + if let Some((_, last_pat_opt, last_expr, _)) = iter.next_back(); + let iter_without_last = iter.clone(); + if let Some((first_attrs, _, first_expr, first_guard)) = iter.next(); + if let Some(b0) = find_bool_lit(&first_expr.kind, is_if_let); + if let Some(b1) = find_bool_lit(&last_expr.kind, is_if_let); if b0 != b1; - let if_guard = &b0_arms[0].guard; - if if_guard.is_none() || b0_arms.len() == 1; - if cx.tcx.hir().attrs(b0_arms[0].hir_id).is_empty(); - if b0_arms[1..].iter() + if first_guard.is_none() || iter.len() == 0; + if first_attrs.is_empty(); + if iter .all(|arm| { - find_bool_lit(&arm.body.kind, desugared).map_or(false, |b| b == b0) && - arm.guard.is_none() && cx.tcx.hir().attrs(arm.hir_id).is_empty() + find_bool_lit(&arm.2.kind, is_if_let).map_or(false, |b| b == b0) && arm.3.is_none() && arm.0.is_empty() }); then { + if let Some(ref last_pat) = last_pat_opt { + if !is_wild(last_pat) { + return false; + } + } + // The suggestion may be incorrect, because some arms can have `cfg` attributes // evaluated into `false` and so such arms will be stripped before. let mut applicability = Applicability::MaybeIncorrect; let pat = { use itertools::Itertools as _; - b0_arms.iter() - .map(|arm| snippet_with_applicability(cx, arm.pat.span, "..", &mut applicability)) + iter_without_last + .filter_map(|arm| { + let pat_span = arm.1?.span; + Some(snippet_with_applicability(cx, pat_span, "..", &mut applicability)) + }) .join(" | ") }; - let pat_and_guard = if let Some(Guard::If(g)) = if_guard { + let pat_and_guard = if let Some(Guard::If(g)) = first_guard { format!("{} if {}", pat, snippet_with_applicability(cx, g.span, "..", &mut applicability)) } else { pat @@ -1342,7 +1402,7 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr cx, MATCH_LIKE_MATCHES_MACRO, expr.span, - &format!("{} expression looks like `matches!` macro", if desugared { "if let .. else" } else { "match" }), + &format!("{} expression looks like `matches!` macro", if is_if_let { "if let .. else" } else { "match" }), "try this", format!( "{}matches!({}, {})", @@ -1360,7 +1420,7 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr } /// Extract a `bool` or `{ bool }` -fn find_bool_lit(ex: &ExprKind<'_>, desugared: bool) -> Option { +fn find_bool_lit(ex: &ExprKind<'_>, is_if_let: bool) -> Option { match ex { ExprKind::Lit(Spanned { node: LitKind::Bool(b), .. @@ -1372,7 +1432,7 @@ fn find_bool_lit(ex: &ExprKind<'_>, desugared: bool) -> Option { .. }, _, - ) if desugared => { + ) if is_if_let => { if let ExprKind::Lit(Spanned { node: LitKind::Bool(b), .. }) = exp.kind @@ -1644,19 +1704,26 @@ fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option]) -> bool { - let mapped = arms - .iter() - .map(|a| { - match a.pat.kind { - PatKind::Ref(..) => Some(true), // &-patterns - PatKind::Wild => Some(false), // an "anything" wildcard is also fine - _ => None, // any other pattern is not fine +fn has_only_ref_pats<'a, 'b, I>(pats: I) -> bool +where + 'b: 'a, + I: Iterator>, +{ + let mut at_least_one_is_true = false; + for opt in pats.map(|pat| match pat.kind { + PatKind::Ref(..) => Some(true), // &-patterns + PatKind::Wild => Some(false), // an "anything" wildcard is also fine + _ => None, // any other pattern is not fine + }) { + if let Some(inner) = opt { + if inner { + at_least_one_is_true = true; } - }) - .collect::>>(); - // look for Some(v) where there's at least one true element - mapped.map_or(false, |v| v.iter().any(|el| *el)) + } else { + return false; + } + } + at_least_one_is_true } pub fn overlapping(ranges: &[SpannedRange]) -> Option<(&SpannedRange, &SpannedRange)> @@ -1745,6 +1812,7 @@ where mod redundant_pattern_match { use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::span_lint_and_then; + use clippy_utils::higher; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type}; use clippy_utils::{is_lang_ctor, is_qpath_def_path, is_trait_method, paths}; @@ -1755,22 +1823,27 @@ mod redundant_pattern_match { use rustc_hir::LangItem::{OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; use rustc_hir::{ intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}, - Arm, Block, Expr, ExprKind, LangItem, MatchSource, Node, PatKind, QPath, + Arm, Block, Expr, ExprKind, LangItem, MatchSource, Node, Pat, PatKind, QPath, }; use rustc_lint::LateContext; use rustc_middle::ty::{self, subst::GenericArgKind, Ty}; use rustc_span::sym; pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Match(op, arms, ref match_source) = &expr.kind { - match match_source { - MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms), - MatchSource::IfLetDesugar { contains_else_clause } => { - find_sugg_for_if_let(cx, expr, op, &arms[0], "if", *contains_else_clause); - }, - MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, &arms[0], "while", false), - _ => {}, - } + if let Some(higher::IfLet { + if_else, + let_pat, + let_expr, + .. + }) = higher::IfLet::ast(cx, expr) + { + find_sugg_for_if_let(cx, expr, let_pat, let_expr, "if", if_else.is_some()) + } + if let ExprKind::Match(op, arms, MatchSource::Normal) = &expr.kind { + find_sugg_for_match(cx, expr, op, arms) + } + if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) { + find_sugg_for_if_let(cx, expr, let_pat, let_expr, "while", false) } } @@ -1924,18 +1997,18 @@ mod redundant_pattern_match { fn find_sugg_for_if_let<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, - op: &'tcx Expr<'tcx>, - arm: &Arm<'_>, + let_pat: &Pat<'_>, + let_expr: &'tcx Expr<'_>, keyword: &'static str, has_else: bool, ) { // also look inside refs - let mut kind = &arm.pat.kind; + let mut kind = &let_pat.kind; // if we have &None for example, peel it so we can detect "if let None = x" if let PatKind::Ref(inner, _mutability) = kind { kind = &inner.kind; } - let op_ty = cx.typeck_results().expr_ty(op); + let op_ty = cx.typeck_results().expr_ty(let_expr); // Determine which function should be used, and the type contained by the corresponding // variant. let (good_method, inner_ty) = match kind { @@ -1989,38 +2062,38 @@ mod redundant_pattern_match { // scrutinee would be, so they have to be considered as well. // e.g. in `if let Some(x) = foo.lock().unwrap().baz.as_ref() { .. }` the lock will be held // for the duration if body. - let needs_drop = type_needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, op); + let needs_drop = type_needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, let_expr); // check that `while_let_on_iterator` lint does not trigger if_chain! { if keyword == "while"; - if let ExprKind::MethodCall(method_path, _, _, _) = op.kind; + if let ExprKind::MethodCall(method_path, _, _, _) = let_expr.kind; if method_path.ident.name == sym::next; - if is_trait_method(cx, op, sym::Iterator); + if is_trait_method(cx, let_expr, sym::Iterator); then { return; } } - let result_expr = match &op.kind { + let result_expr = match &let_expr.kind { ExprKind::AddrOf(_, _, borrowed) => borrowed, - _ => op, + _ => let_expr, }; span_lint_and_then( cx, REDUNDANT_PATTERN_MATCHING, - arm.pat.span, + let_pat.span, &format!("redundant pattern matching, consider using `{}`", good_method), |diag| { - // while let ... = ... { ... } + // if/while let ... = ... { ... } // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ let expr_span = expr.span; - // while let ... = ... { ... } + // if/while let ... = ... { ... } // ^^^ let op_span = result_expr.span.source_callsite(); - // while let ... = ... { ... } + // if/while let ... = ... { ... } // ^^^^^^^^^^^^^^^^^^^ let span = expr_span.until(op_span.shrink_to_hi()); diff --git a/clippy_lints/src/methods/iter_next_slice.rs b/clippy_lints/src/methods/iter_next_slice.rs index a49851de38e1e..6954da67e32c0 100644 --- a/clippy_lints/src/methods/iter_next_slice.rs +++ b/clippy_lints/src/methods/iter_next_slice.rs @@ -18,7 +18,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal // since it is already covered by `&loops::ITER_NEXT_LOOP` let mut parent_expr_opt = get_parent_expr(cx, expr); while let Some(parent_expr) = parent_expr_opt { - if higher::for_loop(parent_expr).is_some() { + if higher::ForLoop::hir(parent_expr).is_some() { return; } parent_expr_opt = get_parent_expr(cx, parent_expr); @@ -29,7 +29,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal if_chain! { if let hir::ExprKind::Index(caller_var, index_expr) = &caller_expr.kind; if let Some(higher::Range { start: Some(start_expr), end: None, limits: ast::RangeLimits::HalfOpen }) - = higher::range(index_expr); + = higher::Range::hir(index_expr); if let hir::ExprKind::Lit(ref start_lit) = &start_expr.kind; if let ast::LitKind::Int(start_idx, _) = start_lit.node; then { diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index d5032c5ba7f29..610152a217f1e 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -53,7 +53,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> { return; } - if let Some((_, arg, body, _)) = higher::for_loop(expr) { + if let Some(higher::ForLoop { arg, body, .. }) = higher::ForLoop::hir(expr) { // A `for` loop lowers to: // ```rust // match ::std::iter::Iterator::next(&mut iter) { diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index 36f2829a5b94e..c9dd94400efb9 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -3,6 +3,7 @@ //! This lint is **warn** by default use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::higher; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::{is_else_clause, is_expn_of}; @@ -77,10 +78,15 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { if e.span.from_expansion() { return; } - if let ExprKind::If(pred, then_block, Some(else_expr)) = e.kind { + if let Some(higher::If { + cond, + then, + r#else: Some(r#else), + }) = higher::If::hir(e) + { let reduce = |ret, not| { let mut applicability = Applicability::MachineApplicable; - let snip = Sugg::hir_with_applicability(cx, pred, "", &mut applicability); + let snip = Sugg::hir_with_applicability(cx, cond, "", &mut applicability); let mut snip = if not { !snip } else { snip }; if ret { @@ -101,8 +107,8 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { applicability, ); }; - if let ExprKind::Block(then_block, _) = then_block.kind { - match (fetch_bool_block(then_block), fetch_bool_expr(else_expr)) { + if let ExprKind::Block(then, _) = then.kind { + match (fetch_bool_block(then), fetch_bool_expr(r#else)) { (RetBool(true), RetBool(true)) | (Bool(true), Bool(true)) => { span_lint( cx, diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index f6254aa715a45..ac21eb5275f0f 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use rustc_ast::ast::{ - Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, FnKind, Item, ItemKind, Local, Pat, - PatKind, + Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, FnKind, Item, ItemKind, Local, Pat, PatKind, }; use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor}; use rustc_lint::{EarlyContext, EarlyLintPass}; diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 7aef3a5f34cf2..d0b0bad5eb1cc 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::higher; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::contains_return_break_continue_macro; @@ -6,7 +7,7 @@ use clippy_utils::{eager_or_lazy, in_macro, is_else_clause, is_lang_ctor}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionSome; -use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp}; +use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, Mutability, PatKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -84,20 +85,20 @@ struct OptionIfLetElseOccurence { /// Extracts the body of a given arm. If the arm contains only an expression, /// then it returns the expression. Otherwise, it returns the entire block -fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> { +fn extract_body_from_expr<'a>(expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> { if let ExprKind::Block( Block { - stmts: statements, - expr: Some(expr), + stmts: block_stmts, + expr: Some(block_expr), .. }, _, - ) = &arm.body.kind + ) = expr.kind { - if let [] = statements { - Some(expr) + if let [] = block_stmts { + Some(block_expr) } else { - Some(arm.body) + Some(expr) } } else { None @@ -121,37 +122,33 @@ 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 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 !in_macro(expr.span); // Don't lint macros, because it behaves weirdly - if let ExprKind::Match(cond_expr, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind; + if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) }) = higher::IfLet::hir(expr); if !is_else_clause(cx.tcx, expr); - if arms.len() == 2; - if !is_result_ok(cx, cond_expr); // Don't lint on Result::ok because a different lint does it already - if let PatKind::TupleStruct(struct_qpath, [inner_pat], _) = &arms[0].pat.kind; + if !is_result_ok(cx, let_expr); // Don't lint on Result::ok because a different lint does it already + if let PatKind::TupleStruct(struct_qpath, [inner_pat], _) = &let_pat.kind; if is_lang_ctor(cx, struct_qpath, OptionSome); if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind; - if !contains_return_break_continue_macro(arms[0].body); - if !contains_return_break_continue_macro(arms[1].body); + if !contains_return_break_continue_macro(if_then); + if !contains_return_break_continue_macro(if_else); then { let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" }; - let some_body = extract_body_from_arm(&arms[0])?; - let none_body = extract_body_from_arm(&arms[1])?; + let some_body = extract_body_from_expr(if_then)?; + let none_body = extract_body_from_expr(if_else)?; let method_sugg = if eager_or_lazy::is_eagerness_candidate(cx, none_body) { "map_or" } else { "map_or_else" }; let capture_name = id.name.to_ident_string(); - let (as_ref, as_mut) = match &cond_expr.kind { + let (as_ref, as_mut) = match &let_expr.kind { ExprKind::AddrOf(_, Mutability::Not, _) => (true, false), ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true), _ => (bind_annotation == &BindingAnnotation::Ref, bind_annotation == &BindingAnnotation::RefMut), }; - let cond_expr = match &cond_expr.kind { + let cond_expr = match let_expr.kind { // Pointer dereferencing happens automatically, so we can omit it in the suggestion ExprKind::Unary(UnOp::Deref, expr) | ExprKind::AddrOf(_, _, expr) => expr, - _ => cond_expr, + _ => let_expr, }; Some(OptionIfLetElseOccurence { option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut), diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index 4534f6e251659..35cff4141a903 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -104,22 +104,25 @@ impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch { } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Match(expr, arms, source) = expr.kind { - match source { - MatchSource::Normal | MatchSource::IfLetDesugar { .. } | MatchSource::WhileLetDesugar => { - if let Some(expr_ty) = cx.typeck_results().node_type_opt(expr.hir_id) { - 'pattern_checks: for arm in arms { - let pat = &arm.pat; - if in_external_macro(cx.sess(), pat.span) { - continue 'pattern_checks; - } - if apply_lint(cx, pat, expr_ty, DerefPossible::Possible) { - break 'pattern_checks; - } - } + if let ExprKind::Match(scrutinee, arms, MatchSource::Normal) = expr.kind { + if let Some(expr_ty) = cx.typeck_results().node_type_opt(scrutinee.hir_id) { + 'pattern_checks: for arm in arms { + let pat = &arm.pat; + if in_external_macro(cx.sess(), pat.span) { + continue 'pattern_checks; } - }, - _ => (), + if apply_lint(cx, pat, expr_ty, DerefPossible::Possible) { + break 'pattern_checks; + } + } + } + } + if let ExprKind::Let(let_pat, let_expr, _) = expr.kind { + if let Some(ref expr_ty) = cx.typeck_results().node_type_opt(let_expr.hir_id) { + if in_external_macro(cx.sess(), let_pat.span) { + return; + } + apply_lint(cx, let_pat, expr_ty, DerefPossible::Possible); } } } diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 0e682c692c7a6..91085c13ac4a4 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::higher; use clippy_utils::is_lang_ctor; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; @@ -7,7 +8,7 @@ use clippy_utils::{eq_expr_value, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; -use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, MatchSource, PatKind, StmtKind}; +use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -50,10 +51,10 @@ impl QuestionMark { /// If it matches, it will suggest to use the question mark operator instead fn check_is_none_and_early_return_none(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let ExprKind::If(if_expr, body, else_) = &expr.kind; - if let ExprKind::MethodCall(segment, _, args, _) = &if_expr.kind; + if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr); + if let ExprKind::MethodCall(segment, _, args, _) = &cond.kind; if segment.ident.name == sym!(is_none); - if Self::expression_returns_none(cx, body); + if Self::expression_returns_none(cx, then); if let Some(subject) = args.get(0); if Self::is_option(cx, subject); @@ -61,9 +62,9 @@ impl QuestionMark { let mut applicability = Applicability::MachineApplicable; let receiver_str = &Sugg::hir_with_applicability(cx, subject, "..", &mut applicability); let mut replacement: Option = None; - if let Some(else_) = else_ { + if let Some(else_inner) = r#else { if_chain! { - if let ExprKind::Block(block, None) = &else_.kind; + if let ExprKind::Block(block, None) = &else_inner.kind; if block.stmts.is_empty(); if let Some(block_expr) = &block.expr; if eq_expr_value(cx, subject, block_expr); @@ -96,25 +97,23 @@ impl QuestionMark { fn check_if_let_some_and_early_return_none(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let ExprKind::Match(subject, arms, source) = &expr.kind; - if *source == MatchSource::IfLetDesugar { contains_else_clause: true }; - if Self::is_option(cx, subject); + if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) }) = higher::IfLet::hir(expr); + if Self::is_option(cx, let_expr); - if let PatKind::TupleStruct(path1, fields, None) = &arms[0].pat.kind; + if let PatKind::TupleStruct(ref path1, fields, None) = let_pat.kind; if is_lang_ctor(cx, path1, OptionSome); if let PatKind::Binding(annot, bind_id, _, _) = fields[0].kind; let by_ref = matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut); - if let ExprKind::Block(block, None) = &arms[0].body.kind; + if let ExprKind::Block(ref block, None) = if_then.kind; if block.stmts.is_empty(); if let Some(trailing_expr) = &block.expr; if path_to_local_id(trailing_expr, bind_id); - if let PatKind::Wild = arms[1].pat.kind; - if Self::expression_returns_none(cx, arms[1].body); + if Self::expression_returns_none(cx, if_else); then { let mut applicability = Applicability::MachineApplicable; - let receiver_str = snippet_with_applicability(cx, subject.span, "..", &mut applicability); + let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability); let replacement = format!( "{}{}?", receiver_str, diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 0179bd48ee3cb..0114a2f97a228 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -329,7 +329,7 @@ fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: if let ExprKind::MethodCall(iter_path, _, iter_args, _) = iter.kind; if iter_path.ident.name == sym::iter; // range expression in `.zip()` call: `0..x.len()` - if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg); + if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg); if is_integer_const(cx, start, 0); // `.len()` call if let ExprKind::MethodCall(len_path, _, len_args, _) = end.kind; @@ -337,7 +337,7 @@ fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: // `.iter()` and `.len()` called on same `Path` if let ExprKind::Path(QPath::Resolved(_, iter_path)) = iter_args[0].kind; if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_args[0].kind; - if SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments); + if SpanlessEq::new(cx).eq_path_segments(&iter_path.segments, &len_path.segments); then { span_lint(cx, RANGE_ZIP_WITH_LEN, @@ -356,7 +356,7 @@ fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { start, end: Some(end), limits: RangeLimits::HalfOpen - }) = higher::range(expr); + }) = higher::Range::hir(expr); if let Some(y) = y_plus_one(cx, end); then { let span = if expr.span.from_expansion() { @@ -401,7 +401,7 @@ fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { // inclusive range minus one: `x..=(y-1)` fn check_inclusive_range_minus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let Some(higher::Range { start, end: Some(end), limits: RangeLimits::Closed }) = higher::range(expr); + if let Some(higher::Range { start, end: Some(end), limits: RangeLimits::Closed }) = higher::Range::hir(expr); if let Some(y) = y_minus_one(cx, end); then { span_lint_and_then( @@ -438,8 +438,8 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { fn is_for_loop_arg(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let mut cur_expr = expr; while let Some(parent_expr) = get_parent_expr(cx, cur_expr) { - match higher::for_loop(parent_expr) { - Some((_, args, _, _)) if args.hir_id == expr.hir_id => return true, + match higher::ForLoop::hir(parent_expr) { + Some(higher::ForLoop { arg, .. }) if arg.hir_id == expr.hir_id => return true, _ => cur_expr = parent_expr, } } @@ -455,7 +455,7 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { } if_chain! { - if let Some(higher::Range { start: Some(start), end: Some(end), limits }) = higher::range(expr); + if let Some(higher::Range { start: Some(start), end: Some(end), limits }) = higher::Range::hir(expr); let ty = cx.typeck_results().expr_ty(start); if let ty::Int(_) | ty::Uint(_) = ty.kind(); if let Some((start_idx, _)) = constant(cx, cx.typeck_results(), start); diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index eeeac35a6d511..530b3396abef6 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -725,7 +725,7 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { BinaryOp(_, box (lhs, rhs)) | CheckedBinaryOp(_, box (lhs, rhs)) => { visit_op(lhs); visit_op(rhs); - } + }, _ => (), } } diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index db4b1002ce129..e153288aa58df 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -212,14 +212,6 @@ fn check_final_expr<'tcx>( check_final_expr(cx, arm.body, Some(arm.body.span), RetReplacement::Block); } }, - MatchSource::IfLetDesugar { - contains_else_clause: true, - } => { - if let ExprKind::Block(ifblock, _) = arms[0].body.kind { - check_block_return(cx, ifblock); - } - check_final_expr(cx, arms[1].body, None, RetReplacement::Empty); - }, _ => (), }, ExprKind::DropTemps(expr) => check_final_expr(cx, expr, None, RetReplacement::Empty), diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index a8e962d1af3fa..44d5ff0b63ad5 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -588,7 +588,7 @@ fn ident_difference_expr_with_base_location( | (ForLoop(_, _, _, _), ForLoop(_, _, _, _)) | (While(_, _, _), While(_, _, _)) | (If(_, _, _), If(_, _, _)) - | (Let(_, _), Let(_, _)) + | (Let(_, _, _), Let(_, _, _)) | (Type(_, _), Type(_, _)) | (Cast(_, _), Cast(_, _)) | (Lit(_), Lit(_)) diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index c8a231341b7e1..d6cf7190abb04 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -67,7 +67,7 @@ impl EarlyLintPass for UnnestedOrPatterns { fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) { - if let ast::ExprKind::Let(pat, _) = &e.kind { + if let ast::ExprKind::Let(pat, _, _) = &e.kind { lint_unnested_or_patterns(cx, pat); } } diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index c5b8acb9982d8..bffd9f3612b0a 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::higher; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{differing_macro_contexts, usage::is_potentially_mutated}; use if_chain::if_chain; @@ -160,11 +161,11 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { if in_external_macro(self.cx.tcx.sess, expr.span) { return; } - if let ExprKind::If(cond, then, els) = &expr.kind { + if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr) { walk_expr(self, cond); self.visit_branch(cond, then, false); - if let Some(els) = els { - self.visit_branch(cond, els, true); + if let Some(else_inner) = r#else { + self.visit_branch(cond, else_inner, true); } } else { // find `unwrap[_err]()` calls: diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index 61fd375a9892c..f93d7782e2511 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -208,6 +208,15 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { print!(" if let ExprKind::"); let current = format!("{}.kind", self.current); match expr.kind { + ExprKind::Let(pat, expr, _) => { + let let_pat = self.next("pat"); + let let_expr = self.next("expr"); + println!(" Let(ref {}, ref {}, _) = {};", let_pat, let_expr, current); + self.current = let_expr; + self.visit_expr(expr); + self.current = let_pat; + self.visit_pat(pat); + }, ExprKind::Box(inner) => { let inner_pat = self.next("inner"); println!("Box(ref {}) = {};", inner_pat, current); diff --git a/clippy_lints/src/utils/inspector.rs b/clippy_lints/src/utils/inspector.rs index f7ddee12dcf64..6bf216cec1670 100644 --- a/clippy_lints/src/utils/inspector.rs +++ b/clippy_lints/src/utils/inspector.rs @@ -66,28 +66,6 @@ impl<'tcx> LateLintPass<'tcx> for DeepCodeInspector { hir::ImplItemKind::TyAlias(_) => println!("associated type"), } } - // fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx - // hir::TraitItem) { - // if !has_attr(&item.attrs) { - // return; - // } - // } - // - // fn check_variant(&mut self, cx: &LateContext<'tcx>, var: &'tcx - // hir::Variant, _: - // &hir::Generics) { - // if !has_attr(&var.node.attrs) { - // return; - // } - // } - // - // fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx - // hir::FieldDef) { - // if !has_attr(&field.attrs) { - // return; - // } - // } - // fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if !has_attr(cx.sess(), cx.tcx.hir().attrs(expr.hir_id)) { @@ -127,13 +105,6 @@ impl<'tcx> LateLintPass<'tcx> for DeepCodeInspector { hir::StmtKind::Expr(e) | hir::StmtKind::Semi(e) => print_expr(cx, e, 0), } } - // fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx - // hir::ForeignItem) { - // if !has_attr(&item.attrs) { - // return; - // } - // } - // } fn has_attr(sess: &Session, attrs: &[Attribute]) -> bool { @@ -171,6 +142,10 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { print_expr(cx, arg, indent + 1); } }, + hir::ExprKind::Let(ref pat, ref expr, _) => { + print_pat(cx, pat, indent + 1); + print_expr(cx, expr, indent + 1); + }, hir::ExprKind::MethodCall(path, _, args, _) => { println!("{}MethodCall", ind); println!("{}method name: {}", ind, path.ident.name); diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index 32fa46f042ce1..95a45fa937f11 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -49,8 +49,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { if_chain! { if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(expr).kind(); if let ty::Slice(..) = ty.kind(); - if let ExprKind::AddrOf(BorrowKind::Ref, mutability, addressee) = expr.kind; - if let Some(vec_args) = higher::vec_macro(cx, addressee); + if let ExprKind::AddrOf(BorrowKind::Ref, mutability, ref addressee) = expr.kind; + if let Some(vec_args) = higher::VecArgs::hir(cx, addressee); then { self.check_vec_macro(cx, &vec_args, mutability, expr.span); } @@ -58,8 +58,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { // search for `for _ in vec![…]` if_chain! { - if let Some((_, arg, _, _)) = higher::for_loop(expr); - if let Some(vec_args) = higher::vec_macro(cx, arg); + if let Some(higher::ForLoop { arg, .. }) = higher::ForLoop::hir(expr); + if let Some(vec_args) = higher::VecArgs::hir(cx, arg); if is_copy(cx, vec_type(cx.typeck_results().expr_ty_adjusted(arg))); then { // report the error around the `vec!` not inside `:` diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index 30c2260d15cac..7ea07a15aea51 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -158,7 +158,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { (Unary(lo, l), Unary(ro, r)) => mem::discriminant(lo) == mem::discriminant(ro) && eq_expr(l, r), (Lit(l), Lit(r)) => l.kind == r.kind, (Cast(l, lt), Cast(r, rt)) | (Type(l, lt), Type(r, rt)) => eq_expr(l, r) && eq_ty(lt, rt), - (Let(lp, le), Let(rp, re)) => eq_pat(lp, rp) && eq_expr(le, re), + (Let(lp, le, _), Let(rp, re, _)) => eq_pat(lp, rp) && eq_expr(le, re), (If(lc, lt, le), If(rc, rt, re)) => eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le, re), (While(lc, lt, ll), While(rc, rt, rl)) => eq_label(ll, rl) && eq_expr(lc, rc) && eq_block(lt, rt), (ForLoop(lp, li, lt, ll), ForLoop(rp, ri, rt, rl)) => { diff --git a/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs index 88b115a63d788..29e2559fc6d60 100644 --- a/clippy_utils/src/eager_or_lazy.rs +++ b/clippy_utils/src/eager_or_lazy.rs @@ -60,6 +60,7 @@ fn identify_some_pure_patterns(expr: &Expr<'_>) -> bool { | ExprKind::MethodCall(..) | ExprKind::Binary(..) | ExprKind::Unary(..) + | ExprKind::Let(..) | ExprKind::Cast(..) | ExprKind::Type(..) | ExprKind::DropTemps(..) diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 884180f0586e3..29b698e56e3c0 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -1,5 +1,4 @@ -//! This module contains functions for retrieve the original AST from lowered -//! `hir`. +//! This module contains functions that retrieves specifiec elements. #![deny(clippy::missing_docs_in_private_items)] @@ -7,142 +6,214 @@ use crate::{is_expn_of, match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast::{self, LitKind}; use rustc_hir as hir; -use rustc_hir::{BorrowKind, Expr, ExprKind, StmtKind, UnOp}; +use rustc_hir::{Block, BorrowKind, Expr, ExprKind, LoopSource, Node, Pat, StmtKind, UnOp}; use rustc_lint::LateContext; use rustc_span::{sym, ExpnKind, Span, Symbol}; -/// Represent a range akin to `ast::ExprKind::Range`. -#[derive(Debug, Copy, Clone)] -pub struct Range<'a> { - /// The lower bound of the range, or `None` for ranges such as `..X`. - pub start: Option<&'a hir::Expr<'a>>, - /// The upper bound of the range, or `None` for ranges such as `X..`. - pub end: Option<&'a hir::Expr<'a>>, - /// Whether the interval is open or closed. - pub limits: ast::RangeLimits, +/// The essential nodes of a desugared for loop as well as the entire span: +/// `for pat in arg { body }` becomes `(pat, arg, body)`. Return `(pat, arg, body, span)`. +pub struct ForLoop<'tcx> { + pub pat: &'tcx hir::Pat<'tcx>, + pub arg: &'tcx hir::Expr<'tcx>, + pub body: &'tcx hir::Expr<'tcx>, + pub span: Span, } -/// Higher a `hir` range to something similar to `ast::ExprKind::Range`. -pub fn range<'a>(expr: &'a hir::Expr<'_>) -> Option> { - /// Finds the field named `name` in the field. Always return `Some` for - /// convenience. - fn get_field<'c>(name: &str, fields: &'c [hir::ExprField<'_>]) -> Option<&'c hir::Expr<'c>> { - let expr = &fields.iter().find(|field| field.ident.name.as_str() == name)?.expr; - - Some(expr) +impl<'tcx> ForLoop<'tcx> { + #[inline] + pub fn hir(expr: &Expr<'tcx>) -> Option { + if_chain! { + if let hir::ExprKind::Match(ref iterexpr, ref arms, hir::MatchSource::ForLoopDesugar) = expr.kind; + if let Some(first_arm) = arms.get(0); + if let hir::ExprKind::Call(_, ref iterargs) = iterexpr.kind; + if let Some(first_arg) = iterargs.get(0); + if iterargs.len() == 1 && arms.len() == 1 && first_arm.guard.is_none(); + if let hir::ExprKind::Loop(ref block, ..) = first_arm.body.kind; + if block.expr.is_none(); + if let [ _, _, ref let_stmt, ref body ] = *block.stmts; + if let hir::StmtKind::Local(ref local) = let_stmt.kind; + if let hir::StmtKind::Expr(ref body_expr) = body.kind; + then { + return Some(Self { + pat: &*local.pat, + arg: first_arg, + body: body_expr, + span: first_arm.span + }); + } + } + None } +} + +pub struct If<'hir> { + pub cond: &'hir Expr<'hir>, + pub r#else: Option<&'hir Expr<'hir>>, + pub then: &'hir Expr<'hir>, +} - match expr.kind { - hir::ExprKind::Call(path, args) - if matches!( - path.kind, - hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, _)) - ) => +impl<'hir> If<'hir> { + #[inline] + pub const fn hir(expr: &Expr<'hir>) -> Option { + if let ExprKind::If( + Expr { + kind: ExprKind::DropTemps(cond), + .. + }, + then, + r#else, + ) = expr.kind { - Some(Range { - start: Some(&args[0]), - end: Some(&args[1]), - limits: ast::RangeLimits::Closed, - }) - }, - hir::ExprKind::Struct(path, fields, None) => match path { - hir::QPath::LangItem(hir::LangItem::RangeFull, _) => Some(Range { - start: None, - end: None, - limits: ast::RangeLimits::HalfOpen, - }), - hir::QPath::LangItem(hir::LangItem::RangeFrom, _) => Some(Range { - start: Some(get_field("start", fields)?), - end: None, - limits: ast::RangeLimits::HalfOpen, - }), - hir::QPath::LangItem(hir::LangItem::Range, _) => Some(Range { - start: Some(get_field("start", fields)?), - end: Some(get_field("end", fields)?), - limits: ast::RangeLimits::HalfOpen, - }), - hir::QPath::LangItem(hir::LangItem::RangeToInclusive, _) => Some(Range { - start: None, - end: Some(get_field("end", fields)?), - limits: ast::RangeLimits::Closed, - }), - hir::QPath::LangItem(hir::LangItem::RangeTo, _) => Some(Range { - start: None, - end: Some(get_field("end", fields)?), - limits: ast::RangeLimits::HalfOpen, - }), - _ => None, - }, - _ => None, + Some(Self { cond, r#else, then }) + } else { + None + } } } -/// Checks if a `let` statement is from a `for` loop desugaring. -pub fn is_from_for_desugar(local: &hir::Local<'_>) -> bool { - // This will detect plain for-loops without an actual variable binding: - // - // ``` - // for x in some_vec { - // // do stuff - // } - // ``` - if_chain! { - if let Some(expr) = local.init; - if let hir::ExprKind::Match(_, _, hir::MatchSource::ForLoopDesugar) = expr.kind; - then { - return true; +pub struct IfLet<'hir> { + pub let_pat: &'hir Pat<'hir>, + pub let_expr: &'hir Expr<'hir>, + pub if_then: &'hir Expr<'hir>, + pub if_else: Option<&'hir Expr<'hir>>, +} + +impl<'hir> IfLet<'hir> { + #[inline] + pub fn ast(cx: &LateContext<'tcx>, expr: &Expr<'hir>) -> Option { + let rslt = Self::hir(expr)?; + Self::is_not_within_while_context(cx, expr)?; + Some(rslt) + } + + #[inline] + pub const fn hir(expr: &Expr<'hir>) -> Option { + if let ExprKind::If( + Expr { + kind: ExprKind::Let(let_pat, let_expr, _), + .. + }, + if_then, + if_else, + ) = expr.kind + { + return Some(Self { + let_pat, + let_expr, + if_then, + if_else, + }); } + None } - // This detects a variable binding in for loop to avoid `let_unit_value` - // lint (see issue #1964). - // - // ``` - // for _ in vec![()] { - // // anything - // } - // ``` - if let hir::LocalSource::ForLoopDesugar = local.source { - return true; + #[inline] + fn is_not_within_while_context(cx: &LateContext<'tcx>, expr: &Expr<'hir>) -> Option<()> { + let hir = cx.tcx.hir(); + let parent = hir.get_parent_node(expr.hir_id); + let parent_parent = hir.get_parent_node(parent); + let parent_parent_node = hir.get(parent_parent); + if let Node::Expr(Expr { + kind: ExprKind::Loop(_, _, LoopSource::While, _), + .. + }) = parent_parent_node + { + return None; + } + Some(()) } +} - false +pub struct IfOrIfLet<'hir> { + pub cond: &'hir Expr<'hir>, + pub r#else: Option<&'hir Expr<'hir>>, + pub then: &'hir Expr<'hir>, } -/// Recover the essential nodes of a desugared for loop as well as the entire span: -/// `for pat in arg { body }` becomes `(pat, arg, body)`. Return `(pat, arg, body, span)`. -pub fn for_loop<'tcx>( - expr: &'tcx hir::Expr<'tcx>, -) -> Option<(&hir::Pat<'_>, &'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>, Span)> { - if_chain! { - if let hir::ExprKind::Match(iterexpr, arms, hir::MatchSource::ForLoopDesugar) = expr.kind; - if let hir::ExprKind::Call(_, iterargs) = iterexpr.kind; - if iterargs.len() == 1 && arms.len() == 1 && arms[0].guard.is_none(); - if let hir::ExprKind::Loop(block, ..) = arms[0].body.kind; - if block.expr.is_none(); - if let [ _, _, ref let_stmt, ref body ] = *block.stmts; - if let hir::StmtKind::Local(local) = let_stmt.kind; - if let hir::StmtKind::Expr(expr) = body.kind; - then { - return Some((&*local.pat, &iterargs[0], expr, arms[0].span)); +impl<'hir> IfOrIfLet<'hir> { + #[inline] + pub const fn hir(expr: &Expr<'hir>) -> Option { + if let ExprKind::If(cond, then, r#else) = expr.kind { + if let ExprKind::DropTemps(new_cond) = cond.kind { + return Some(Self { + cond: new_cond, + r#else, + then, + }); + } + if let ExprKind::Let(..) = cond.kind { + return Some(Self { cond, r#else, then }); + } } + None } - None } -/// Recover the essential nodes of a desugared while loop: -/// `while cond { body }` becomes `(cond, body)`. -pub fn while_loop<'tcx>(expr: &'tcx hir::Expr<'tcx>) -> Option<(&'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>)> { - if_chain! { - if let hir::ExprKind::Loop(hir::Block { expr: Some(expr), .. }, _, hir::LoopSource::While, _) = &expr.kind; - if let hir::ExprKind::Match(cond, arms, hir::MatchSource::WhileDesugar) = &expr.kind; - if let hir::ExprKind::DropTemps(cond) = &cond.kind; - if let [hir::Arm { body, .. }, ..] = &arms[..]; - then { - return Some((cond, body)); +/// Represent a range akin to `ast::ExprKind::Range`. +#[derive(Debug, Copy, Clone)] +pub struct Range<'a> { + /// The lower bound of the range, or `None` for ranges such as `..X`. + pub start: Option<&'a hir::Expr<'a>>, + /// The upper bound of the range, or `None` for ranges such as `X..`. + pub end: Option<&'a hir::Expr<'a>>, + /// Whether the interval is open or closed. + pub limits: ast::RangeLimits, +} + +impl<'a> Range<'a> { + /// Higher a `hir` range to something similar to `ast::ExprKind::Range`. + pub fn hir(expr: &'a hir::Expr<'_>) -> Option> { + /// Finds the field named `name` in the field. Always return `Some` for + /// convenience. + fn get_field<'c>(name: &str, fields: &'c [hir::ExprField<'_>]) -> Option<&'c hir::Expr<'c>> { + let expr = &fields.iter().find(|field| field.ident.name.as_str() == name)?.expr; + Some(expr) + } + + match expr.kind { + hir::ExprKind::Call(ref path, ref args) + if matches!( + path.kind, + hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, _)) + ) => + { + Some(Range { + start: Some(&args[0]), + end: Some(&args[1]), + limits: ast::RangeLimits::Closed, + }) + }, + hir::ExprKind::Struct(ref path, ref fields, None) => match path { + hir::QPath::LangItem(hir::LangItem::RangeFull, _) => Some(Range { + start: None, + end: None, + limits: ast::RangeLimits::HalfOpen, + }), + hir::QPath::LangItem(hir::LangItem::RangeFrom, _) => Some(Range { + start: Some(get_field("start", fields)?), + end: None, + limits: ast::RangeLimits::HalfOpen, + }), + hir::QPath::LangItem(hir::LangItem::Range, _) => Some(Range { + start: Some(get_field("start", fields)?), + end: Some(get_field("end", fields)?), + limits: ast::RangeLimits::HalfOpen, + }), + hir::QPath::LangItem(hir::LangItem::RangeToInclusive, _) => Some(Range { + start: None, + end: Some(get_field("end", fields)?), + limits: ast::RangeLimits::Closed, + }), + hir::QPath::LangItem(hir::LangItem::RangeTo, _) => Some(Range { + start: None, + end: Some(get_field("end", fields)?), + limits: ast::RangeLimits::HalfOpen, + }), + _ => None, + }, + _ => None, } } - None } /// Represent the pre-expansion arguments of a `vec!` invocation. @@ -153,41 +224,157 @@ pub enum VecArgs<'a> { Vec(&'a [hir::Expr<'a>]), } -/// Returns the arguments of the `vec!` macro if this expression was expanded -/// from `vec!`. -pub fn vec_macro<'e>(cx: &LateContext<'_>, expr: &'e hir::Expr<'_>) -> Option> { - if_chain! { - if let hir::ExprKind::Call(fun, args) = expr.kind; - if let hir::ExprKind::Path(ref qpath) = fun.kind; - if is_expn_of(fun.span, "vec").is_some(); - if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); - then { - return if match_def_path(cx, fun_def_id, &paths::VEC_FROM_ELEM) && args.len() == 2 { - // `vec![elem; size]` case - Some(VecArgs::Repeat(&args[0], &args[1])) - } - else if match_def_path(cx, fun_def_id, &paths::SLICE_INTO_VEC) && args.len() == 1 { - // `vec![a, b, c]` case - if_chain! { - if let hir::ExprKind::Box(boxed) = args[0].kind; - if let hir::ExprKind::Array(args) = boxed.kind; - then { - return Some(VecArgs::Vec(&*args)); - } +impl<'a> VecArgs<'a> { + /// Returns the arguments of the `vec!` macro if this expression was expanded + /// from `vec!`. + pub fn hir(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> Option> { + if_chain! { + if let hir::ExprKind::Call(ref fun, ref args) = expr.kind; + if let hir::ExprKind::Path(ref qpath) = fun.kind; + if is_expn_of(fun.span, "vec").is_some(); + if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); + then { + return if match_def_path(cx, fun_def_id, &paths::VEC_FROM_ELEM) && args.len() == 2 { + // `vec![elem; size]` case + Some(VecArgs::Repeat(&args[0], &args[1])) } + else if match_def_path(cx, fun_def_id, &paths::SLICE_INTO_VEC) && args.len() == 1 { + // `vec![a, b, c]` case + if_chain! { + if let hir::ExprKind::Box(ref boxed) = args[0].kind; + if let hir::ExprKind::Array(ref args) = boxed.kind; + then { + return Some(VecArgs::Vec(&*args)); + } + } - None + None + } + else if match_def_path(cx, fun_def_id, &paths::VEC_NEW) && args.is_empty() { + Some(VecArgs::Vec(&[])) + } + else { + None + }; } - else if match_def_path(cx, fun_def_id, &paths::VEC_NEW) && args.is_empty() { - Some(VecArgs::Vec(&[])) + } + + None + } +} + +pub struct While<'hir> { + pub if_cond: &'hir Expr<'hir>, + pub if_then: &'hir Expr<'hir>, + pub if_else: Option<&'hir Expr<'hir>>, +} + +impl<'hir> While<'hir> { + #[inline] + pub const fn hir(expr: &Expr<'hir>) -> Option { + if let ExprKind::Loop( + Block { + expr: + Some(Expr { + kind: + ExprKind::If( + Expr { + kind: ExprKind::DropTemps(if_cond), + .. + }, + if_then, + if_else_ref, + ), + .. + }), + .. + }, + _, + LoopSource::While, + _, + ) = expr.kind + { + let if_else = *if_else_ref; + return Some(Self { + if_cond, + if_then, + if_else, + }); + } + None + } +} + +pub struct WhileLet<'hir> { + pub if_expr: &'hir Expr<'hir>, + pub let_pat: &'hir Pat<'hir>, + pub let_expr: &'hir Expr<'hir>, + pub if_then: &'hir Expr<'hir>, + pub if_else: Option<&'hir Expr<'hir>>, +} + +impl<'hir> WhileLet<'hir> { + #[inline] + pub const fn hir(expr: &Expr<'hir>) -> Option { + if let ExprKind::Loop( + Block { + expr: Some(if_expr), .. + }, + _, + LoopSource::While, + _, + ) = expr.kind + { + if let Expr { + kind: + ExprKind::If( + Expr { + kind: ExprKind::Let(let_pat, let_expr, _), + .. + }, + if_then, + if_else_ref, + ), + .. + } = if_expr + { + let if_else = *if_else_ref; + return Some(Self { + if_expr, + let_pat, + let_expr, + if_then, + if_else, + }); } - else { - None - }; } + None } +} - None +/// Converts a hir binary operator to the corresponding `ast` type. +#[must_use] +pub fn binop(op: hir::BinOpKind) -> ast::BinOpKind { + match op { + hir::BinOpKind::Eq => ast::BinOpKind::Eq, + hir::BinOpKind::Ge => ast::BinOpKind::Ge, + hir::BinOpKind::Gt => ast::BinOpKind::Gt, + hir::BinOpKind::Le => ast::BinOpKind::Le, + hir::BinOpKind::Lt => ast::BinOpKind::Lt, + hir::BinOpKind::Ne => ast::BinOpKind::Ne, + hir::BinOpKind::Or => ast::BinOpKind::Or, + hir::BinOpKind::Add => ast::BinOpKind::Add, + hir::BinOpKind::And => ast::BinOpKind::And, + hir::BinOpKind::BitAnd => ast::BinOpKind::BitAnd, + hir::BinOpKind::BitOr => ast::BinOpKind::BitOr, + hir::BinOpKind::BitXor => ast::BinOpKind::BitXor, + hir::BinOpKind::Div => ast::BinOpKind::Div, + hir::BinOpKind::Mul => ast::BinOpKind::Mul, + hir::BinOpKind::Rem => ast::BinOpKind::Rem, + hir::BinOpKind::Shl => ast::BinOpKind::Shl, + hir::BinOpKind::Shr => ast::BinOpKind::Shr, + hir::BinOpKind::Sub => ast::BinOpKind::Sub, + } } /// Extract args from an assert-like macro. @@ -218,8 +405,8 @@ pub fn extract_assert_macro_args<'tcx>(e: &'tcx Expr<'tcx>) -> Option { } } } + +/// Checks if a `let` statement is from a `for` loop desugaring. +pub fn is_from_for_desugar(local: &hir::Local<'_>) -> bool { + // This will detect plain for-loops without an actual variable binding: + // + // ``` + // for x in some_vec { + // // do stuff + // } + // ``` + if_chain! { + if let Some(ref expr) = local.init; + if let hir::ExprKind::Match(_, _, hir::MatchSource::ForLoopDesugar) = expr.kind; + then { + return true; + } + } + + // This detects a variable binding in for loop to avoid `let_unit_value` + // lint (see issue #1964). + // + // ``` + // for _ in vec![()] { + // // anything + // } + // ``` + if let hir::LocalSource::ForLoopDesugar = local.source { + return true; + } + + false +} diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index fd70553e064b6..a44f2df2fd631 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -232,6 +232,9 @@ impl HirEqInterExpr<'_, '_, '_> { (&ExprKind::If(lc, lt, ref le), &ExprKind::If(rc, rt, ref re)) => { self.eq_expr(lc, rc) && self.eq_expr(&**lt, &**rt) && both(le, re, |l, r| self.eq_expr(l, r)) }, + (&ExprKind::Let(ref lp, ref le, _), &ExprKind::Let(ref rp, ref re, _)) => { + self.eq_pat(lp, rp) && self.eq_expr(le, re) + }, (&ExprKind::Lit(ref l), &ExprKind::Lit(ref r)) => l.node == r.node, (&ExprKind::Loop(lb, ref ll, ref lls, _), &ExprKind::Loop(rb, ref rl, ref rls, _)) => { lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.name == r.ident.name) @@ -665,6 +668,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } } }, + ExprKind::Let(ref pat, ref expr, _) => { + self.hash_expr(expr); + self.hash_pat(pat); + }, ExprKind::LlvmInlineAsm(..) | ExprKind::Err => {}, ExprKind::Lit(ref l) => { l.node.hash(&mut self.s); diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 1d59d6bfea1b9..82bfce8fe789e 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -961,17 +961,6 @@ pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { let map = tcx.hir(); let mut iter = map.parent_iter(expr.hir_id); match iter.next() { - Some((arm_id, Node::Arm(..))) => matches!( - iter.next(), - Some(( - _, - Node::Expr(Expr { - kind: ExprKind::Match(_, [_, else_arm], MatchSource::IfLetDesugar { .. }), - .. - }) - )) - if else_arm.hir_id == arm_id - ), Some(( _, Node::Expr(Expr { @@ -1370,15 +1359,15 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, let mut conds = Vec::new(); let mut blocks: Vec<&Block<'_>> = Vec::new(); - while let ExprKind::If(cond, then_expr, ref else_expr) = expr.kind { - conds.push(cond); - if let ExprKind::Block(block, _) = then_expr.kind { + while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) { + conds.push(&*cond); + if let ExprKind::Block(ref block, _) = then.kind { blocks.push(block); } else { panic!("ExprKind::If node is not an ExprKind::Block"); } - if let Some(else_expr) = *else_expr { + if let Some(ref else_expr) = r#else { expr = else_expr; } else { break; diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 3bd75b10e9058..3b494e1fc8535 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -116,7 +116,7 @@ impl<'a> Sugg<'a> { /// Generate a suggestion for an expression with the given snippet. This is used by the `hir_*` /// function variants of `Sugg`, since these use different snippet functions. fn hir_from_snippet(expr: &hir::Expr<'_>, snippet: Cow<'a, str>) -> Self { - if let Some(range) = higher::range(expr) { + if let Some(range) = higher::Range::hir(expr) { let op = match range.limits { ast::RangeLimits::HalfOpen => AssocOp::DotDot, ast::RangeLimits::Closed => AssocOp::DotDotEq, @@ -128,6 +128,7 @@ impl<'a> Sugg<'a> { hir::ExprKind::AddrOf(..) | hir::ExprKind::Box(..) | hir::ExprKind::If(..) + | hir::ExprKind::Let(..) | hir::ExprKind::Closure(..) | hir::ExprKind::Unary(..) | hir::ExprKind::Match(..) => Sugg::MaybeParen(snippet), diff --git a/tests/ui/author/if.stdout b/tests/ui/author/if.stdout index 502b38545b8ef..1653de9a6f26d 100644 --- a/tests/ui/author/if.stdout +++ b/tests/ui/author/if.stdout @@ -12,7 +12,8 @@ if_chain! { if let ExprKind::Lit(ref lit1) = right.kind; if let LitKind::Int(2, _) = lit1.node; if block.expr.is_none(); - if let ExprKind::Lit(ref lit2) = cond.kind; + if let ExprKind::DropTemps(ref expr) = cond.kind; + if let ExprKind::Lit(ref lit2) = expr.kind; if let LitKind::Bool(true) = lit2.node; if let ExprKind::Block(ref block1) = then.kind; if block1.stmts.len() == 1; diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr index 7797888490089..f96917f583344 100644 --- a/tests/ui/collapsible_match.stderr +++ b/tests/ui/collapsible_match.stderr @@ -35,7 +35,7 @@ LL | Ok(val) => match val { LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: unnecessary nested match +error: unnecessary nested `if let` or `match` --> $DIR/collapsible_match.rs:25:9 | LL | / if let Some(n) = val { @@ -51,7 +51,7 @@ LL | if let Ok(val) = res_opt { LL | if let Some(n) = val { | ^^^^^^^ with this pattern -error: unnecessary nested match +error: unnecessary nested `if let` or `match` --> $DIR/collapsible_match.rs:32:9 | LL | / if let Some(n) = val { @@ -87,7 +87,7 @@ LL | match val { LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: unnecessary nested match +error: unnecessary nested `if let` or `match` --> $DIR/collapsible_match.rs:52:13 | LL | / if let Some(n) = val { @@ -121,7 +121,7 @@ LL | match val { LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: unnecessary nested match +error: unnecessary nested `if let` or `match` --> $DIR/collapsible_match.rs:72:13 | LL | / if let Some(n) = val { diff --git a/tests/ui/crashes/ice-7410.rs b/tests/ui/crashes/ice-7410.rs index aaa422d88c3e0..85fa421032191 100644 --- a/tests/ui/crashes/ice-7410.rs +++ b/tests/ui/crashes/ice-7410.rs @@ -4,6 +4,7 @@ #![feature(lang_items, start, libc)] #![no_std] +#![allow(clippy::if_same_then_else)] #![allow(clippy::redundant_pattern_matching)] use core::panic::PanicInfo; diff --git a/tests/ui/crashes/issues_loop_mut_cond.rs b/tests/ui/crashes/issues_loop_mut_cond.rs index bb238c81ebc05..553c840f9b081 100644 --- a/tests/ui/crashes/issues_loop_mut_cond.rs +++ b/tests/ui/crashes/issues_loop_mut_cond.rs @@ -1,3 +1,4 @@ +#![allow(clippy::blocks_in_if_conditions)] #![allow(dead_code)] /// Issue: https://github.com/rust-lang/rust-clippy/issues/2596 diff --git a/tests/ui/infinite_loop.rs b/tests/ui/infinite_loop.rs index 3d8fb8507e515..e518b2677b7bf 100644 --- a/tests/ui/infinite_loop.rs +++ b/tests/ui/infinite_loop.rs @@ -1,3 +1,5 @@ +#![allow(clippy::blocks_in_if_conditions)] + fn fn_val(i: i32) -> i32 { unimplemented!() } diff --git a/tests/ui/infinite_loop.stderr b/tests/ui/infinite_loop.stderr index 1fcb29eff18e4..2736753c14b6e 100644 --- a/tests/ui/infinite_loop.stderr +++ b/tests/ui/infinite_loop.stderr @@ -1,5 +1,5 @@ error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:21:11 + --> $DIR/infinite_loop.rs:23:11 | LL | while y < 10 { | ^^^^^^ @@ -8,7 +8,7 @@ LL | while y < 10 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:26:11 + --> $DIR/infinite_loop.rs:28:11 | LL | while y < 10 && x < 3 { | ^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | while y < 10 && x < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:33:11 + --> $DIR/infinite_loop.rs:35:11 | LL | while !cond { | ^^^^^ @@ -24,7 +24,7 @@ LL | while !cond { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:77:11 + --> $DIR/infinite_loop.rs:79:11 | LL | while i < 3 { | ^^^^^ @@ -32,7 +32,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:82:11 + --> $DIR/infinite_loop.rs:84:11 | LL | while i < 3 && j > 0 { | ^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | while i < 3 && j > 0 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:86:11 + --> $DIR/infinite_loop.rs:88:11 | LL | while i < 3 { | ^^^^^ @@ -48,7 +48,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:101:11 + --> $DIR/infinite_loop.rs:103:11 | LL | while i < 3 { | ^^^^^ @@ -56,7 +56,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:106:11 + --> $DIR/infinite_loop.rs:108:11 | LL | while i < 3 { | ^^^^^ @@ -64,7 +64,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:172:15 + --> $DIR/infinite_loop.rs:174:15 | LL | while self.count < n { | ^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | while self.count < n { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:180:11 + --> $DIR/infinite_loop.rs:182:11 | LL | while y < 10 { | ^^^^^^ @@ -82,7 +82,7 @@ LL | while y < 10 { = help: rewrite it as `if cond { loop { } }` error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:187:11 + --> $DIR/infinite_loop.rs:189:11 | LL | while y < 10 { | ^^^^^^ diff --git a/tests/ui/match_overlapping_arm.rs b/tests/ui/match_overlapping_arm.rs index 44c51e8112a7d..c84e31ea482a4 100644 --- a/tests/ui/match_overlapping_arm.rs +++ b/tests/ui/match_overlapping_arm.rs @@ -2,6 +2,7 @@ #![feature(half_open_range_patterns)] #![warn(clippy::match_overlapping_arm)] #![allow(clippy::redundant_pattern_matching)] +#![allow(clippy::if_same_then_else)] /// Tests for match_overlapping_arm diff --git a/tests/ui/match_overlapping_arm.stderr b/tests/ui/match_overlapping_arm.stderr index f25a66d634e88..359fa49f51be7 100644 --- a/tests/ui/match_overlapping_arm.stderr +++ b/tests/ui/match_overlapping_arm.stderr @@ -1,60 +1,60 @@ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:12:9 + --> $DIR/match_overlapping_arm.rs:13:9 | LL | 0..=10 => println!("0 ... 10"), | ^^^^^^ | = note: `-D clippy::match-overlapping-arm` implied by `-D warnings` note: overlaps with this - --> $DIR/match_overlapping_arm.rs:13:9 + --> $DIR/match_overlapping_arm.rs:14:9 | LL | 0..=11 => println!("0 ... 11"), | ^^^^^^ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:18:9 + --> $DIR/match_overlapping_arm.rs:19:9 | LL | 0..=5 => println!("0 ... 5"), | ^^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:20:9 + --> $DIR/match_overlapping_arm.rs:21:9 | LL | FOO..=11 => println!("0 ... 11"), | ^^^^^^^^ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:55:9 + --> $DIR/match_overlapping_arm.rs:56:9 | LL | 0..11 => println!("0 .. 11"), | ^^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:56:9 + --> $DIR/match_overlapping_arm.rs:57:9 | LL | 0..=11 => println!("0 ... 11"), | ^^^^^^ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:80:9 + --> $DIR/match_overlapping_arm.rs:81:9 | LL | 0..=10 => println!("0 ... 10"), | ^^^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:79:9 + --> $DIR/match_overlapping_arm.rs:80:9 | LL | 5..14 => println!("5 .. 14"), | ^^^^^ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:85:9 + --> $DIR/match_overlapping_arm.rs:86:9 | LL | 0..7 => println!("0 .. 7"), | ^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:86:9 + --> $DIR/match_overlapping_arm.rs:87:9 | LL | 0..=10 => println!("0 ... 10"), | ^^^^^^ From 997ddbbfd8adf3a56307c817d562fc6d07bcbd6c Mon Sep 17 00:00:00 2001 From: Lucas Kent Date: Sat, 31 Jul 2021 17:50:12 +1000 Subject: [PATCH 17/94] Lintcheck always copies in a fresh crate when provided with a crate path but skips directories containing CACHEDIR.TAG e.g. the target/ dir --- lintcheck/Cargo.toml | 1 + lintcheck/src/main.rs | 46 ++++++++++++++++++++++++++----------------- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/lintcheck/Cargo.toml b/lintcheck/Cargo.toml index 8c33fa372eccb..ada033de6e3ab 100644 --- a/lintcheck/Cargo.toml +++ b/lintcheck/Cargo.toml @@ -19,6 +19,7 @@ serde_json = {version = "1.0"} tar = {version = "0.4.30"} toml = {version = "0.5"} ureq = {version = "2.0.0-rc3"} +walkdir = {version = "2.3.2"} [features] deny-warnings = [] diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index 7d2f3173fb0e7..f1e03ba42966d 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -21,6 +21,7 @@ use clap::{App, Arg, ArgMatches}; use rayon::prelude::*; use serde::{Deserialize, Serialize}; use serde_json::Value; +use walkdir::{DirEntry, WalkDir}; #[cfg(not(windows))] const CLIPPY_DRIVER_PATH: &str = "target/debug/clippy-driver"; @@ -193,32 +194,41 @@ impl CrateSource { } }, CrateSource::Path { name, path, options } => { - use fs_extra::dir; - - // simply copy the entire directory into our target dir - let copy_dest = PathBuf::from(format!("{}/", LINTCHECK_SOURCES)); + // copy path into the dest_crate_root but skip directories that contain a CACHEDIR.TAG file. + // The target/ directory contains a CACHEDIR.TAG file so it is the most commonly skipped directory + // as a result of this filter. + let dest_crate_root = PathBuf::from(LINTCHECK_SOURCES).join(name); + if dest_crate_root.exists() { + println!("Deleting existing directory at {:?}", dest_crate_root); + std::fs::remove_dir_all(&dest_crate_root).unwrap(); + } - // the source path of the crate we copied, ${copy_dest}/crate_name - let crate_root = copy_dest.join(name); // .../crates/local_crate + println!("Copying {:?} to {:?}", path, dest_crate_root); - if crate_root.exists() { - println!( - "Not copying {} to {}, destination already exists", - path.display(), - crate_root.display() - ); - } else { - println!("Copying {} to {}", path.display(), copy_dest.display()); + fn is_cache_dir(entry: &DirEntry) -> bool { + std::fs::read(entry.path().join("CACHEDIR.TAG")) + .map(|x| x.starts_with(b"Signature: 8a477f597d28d172789f06886806bc55")) + .unwrap_or(false) + } - dir::copy(path, ©_dest, &dir::CopyOptions::new()).unwrap_or_else(|_| { - panic!("Failed to copy from {}, to {}", path.display(), crate_root.display()) - }); + for entry in WalkDir::new(path).into_iter().filter_entry(|e| !is_cache_dir(e)) { + let entry = entry.unwrap(); + let entry_path = entry.path(); + let relative_entry_path = entry_path.strip_prefix(path).unwrap(); + let dest_path = dest_crate_root.join(relative_entry_path); + let metadata = entry_path.symlink_metadata().unwrap(); + + if metadata.is_dir() { + std::fs::create_dir(dest_path).unwrap(); + } else if metadata.is_file() { + std::fs::copy(entry_path, dest_path).unwrap(); + } } Crate { version: String::from("local"), name: name.clone(), - path: crate_root, + path: dest_crate_root, options: options.clone(), } }, From f0444d73def161f7f0fda7b3f5a13e9908e9c550 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 15 Aug 2021 20:25:10 -0400 Subject: [PATCH 18/94] Use `each_binding_or_first` in `capture_local_usage` --- clippy_utils/src/lib.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index b40b42fa6d3b6..bd229402f4182 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -733,16 +733,18 @@ impl std::ops::BitOrAssign for CaptureKind { pub fn capture_local_usage(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind { fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind { let mut capture = CaptureKind::Ref(Mutability::Not); - pat.each_binding(|_, id, span, _| { - match cx.typeck_results().extract_binding_mode(cx.sess(), id, span).unwrap() { - BindingMode::BindByValue(_) if !is_copy(cx, cx.typeck_results().node_type(id)) => { - capture = CaptureKind::Value; - }, - BindingMode::BindByReference(Mutability::Mut) if capture != CaptureKind::Value => { - capture = CaptureKind::Ref(Mutability::Mut); - }, - _ => (), - } + pat.each_binding_or_first(&mut |_, id, span, _| match cx + .typeck_results() + .extract_binding_mode(cx.sess(), id, span) + .unwrap() + { + BindingMode::BindByValue(_) if !is_copy(cx, cx.typeck_results().node_type(id)) => { + capture = CaptureKind::Value; + }, + BindingMode::BindByReference(Mutability::Mut) if capture != CaptureKind::Value => { + capture = CaptureKind::Ref(Mutability::Mut); + }, + _ => (), }); capture } From 295225b660e1ddfd4a476a14aa12b37ae140f91d Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Wed, 2 Dec 2020 15:16:12 -0800 Subject: [PATCH 19/94] Uplift the `invalid_atomic_ordering` lint from clippy to rustc - Deprecate clippy::invalid_atomic_ordering - Use rustc_diagnostic_item for the orderings in the invalid_atomic_ordering lint - Reduce code duplication - Give up on making enum variants diagnostic items and just look for `Ordering` instead I ran into tons of trouble with this because apparently the change to store HIR attrs in a side table also gave the DefIds of the constructor instead of the variant itself. So I had to change `matches_ordering` to also check the grandparent of the defid as well. - Rename `atomic_ordering_x` symbols to just the name of the variant - Fix typos in checks - there were a few places that said "may not be Release" in the diagnostic but actually checked for SeqCst in the lint. - Make constant items const - Use fewer diagnostic items - Only look at arguments after making sure the method matches This prevents an ICE when there aren't enough arguments. - Ignore trait methods - Only check Ctors instead of going through `qpath_res` The functions take values, so this couldn't ever be anything else. - Add if_chain to allowed dependencies - Fix grammar - Remove unnecessary allow --- clippy_lints/src/atomic_ordering.rs | 230 ------------------ clippy_lints/src/lib.rs | 6 +- tests/ui/atomic_ordering_bool.rs | 25 -- tests/ui/atomic_ordering_bool.stderr | 35 --- tests/ui/atomic_ordering_exchange.rs | 45 ---- tests/ui/atomic_ordering_exchange.stderr | 131 ---------- tests/ui/atomic_ordering_exchange_weak.rs | 47 ---- tests/ui/atomic_ordering_exchange_weak.stderr | 131 ---------- tests/ui/atomic_ordering_fence.rs | 20 -- tests/ui/atomic_ordering_fence.stderr | 19 -- tests/ui/atomic_ordering_fetch_update.rs | 45 ---- tests/ui/atomic_ordering_fetch_update.stderr | 131 ---------- tests/ui/atomic_ordering_int.rs | 86 ------- tests/ui/atomic_ordering_int.stderr | 163 ------------- tests/ui/atomic_ordering_ptr.rs | 27 -- tests/ui/atomic_ordering_ptr.stderr | 35 --- tests/ui/atomic_ordering_uint.rs | 86 ------- tests/ui/atomic_ordering_uint.stderr | 163 ------------- tests/ui/deprecated.rs | 1 + tests/ui/deprecated.stderr | 8 +- 20 files changed, 9 insertions(+), 1425 deletions(-) delete mode 100644 clippy_lints/src/atomic_ordering.rs delete mode 100644 tests/ui/atomic_ordering_bool.rs delete mode 100644 tests/ui/atomic_ordering_bool.stderr delete mode 100644 tests/ui/atomic_ordering_exchange.rs delete mode 100644 tests/ui/atomic_ordering_exchange.stderr delete mode 100644 tests/ui/atomic_ordering_exchange_weak.rs delete mode 100644 tests/ui/atomic_ordering_exchange_weak.stderr delete mode 100644 tests/ui/atomic_ordering_fence.rs delete mode 100644 tests/ui/atomic_ordering_fence.stderr delete mode 100644 tests/ui/atomic_ordering_fetch_update.rs delete mode 100644 tests/ui/atomic_ordering_fetch_update.stderr delete mode 100644 tests/ui/atomic_ordering_int.rs delete mode 100644 tests/ui/atomic_ordering_int.stderr delete mode 100644 tests/ui/atomic_ordering_ptr.rs delete mode 100644 tests/ui/atomic_ordering_ptr.stderr delete mode 100644 tests/ui/atomic_ordering_uint.rs delete mode 100644 tests/ui/atomic_ordering_uint.stderr diff --git a/clippy_lints/src/atomic_ordering.rs b/clippy_lints/src/atomic_ordering.rs deleted file mode 100644 index cece28e8b3c3f..0000000000000 --- a/clippy_lints/src/atomic_ordering.rs +++ /dev/null @@ -1,230 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::match_def_path; -use if_chain::if_chain; -use rustc_hir::def_id::DefId; -use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -declare_clippy_lint! { - /// ### What it does - /// Checks for usage of invalid atomic - /// ordering in atomic loads/stores/exchanges/updates and - /// memory fences. - /// - /// ### Why is this bad? - /// Using an invalid atomic ordering - /// will cause a panic at run-time. - /// - /// ### Example - /// ```rust,no_run - /// # use std::sync::atomic::{self, AtomicU8, Ordering}; - /// - /// let x = AtomicU8::new(0); - /// - /// // Bad: `Release` and `AcqRel` cannot be used for `load`. - /// let _ = x.load(Ordering::Release); - /// let _ = x.load(Ordering::AcqRel); - /// - /// // Bad: `Acquire` and `AcqRel` cannot be used for `store`. - /// x.store(1, Ordering::Acquire); - /// x.store(2, Ordering::AcqRel); - /// - /// // Bad: `Relaxed` cannot be used as a fence's ordering. - /// atomic::fence(Ordering::Relaxed); - /// atomic::compiler_fence(Ordering::Relaxed); - /// - /// // Bad: `Release` and `AcqRel` are both always invalid - /// // for the failure ordering (the last arg). - /// let _ = x.compare_exchange(1, 2, Ordering::SeqCst, Ordering::Release); - /// let _ = x.compare_exchange_weak(2, 3, Ordering::AcqRel, Ordering::AcqRel); - /// - /// // Bad: The failure ordering is not allowed to be - /// // stronger than the success order, and `SeqCst` is - /// // stronger than `Relaxed`. - /// let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |val| Some(val + val)); - /// ``` - pub INVALID_ATOMIC_ORDERING, - correctness, - "usage of invalid atomic ordering in atomic operations and memory fences" -} - -declare_lint_pass!(AtomicOrdering => [INVALID_ATOMIC_ORDERING]); - -const ATOMIC_TYPES: [&str; 12] = [ - "AtomicBool", - "AtomicI8", - "AtomicI16", - "AtomicI32", - "AtomicI64", - "AtomicIsize", - "AtomicPtr", - "AtomicU8", - "AtomicU16", - "AtomicU32", - "AtomicU64", - "AtomicUsize", -]; - -fn type_is_atomic(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let ty::Adt(&ty::AdtDef { did, .. }, _) = cx.typeck_results().expr_ty(expr).kind() { - ATOMIC_TYPES - .iter() - .any(|ty| match_def_path(cx, did, &["core", "sync", "atomic", ty])) - } else { - false - } -} - -fn match_ordering_def_path(cx: &LateContext<'_>, did: DefId, orderings: &[&str]) -> bool { - orderings - .iter() - .any(|ordering| match_def_path(cx, did, &["core", "sync", "atomic", "Ordering", ordering])) -} - -fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) { - if_chain! { - if let ExprKind::MethodCall(method_path, _, args, _) = &expr.kind; - let method = method_path.ident.name.as_str(); - if type_is_atomic(cx, &args[0]); - if method == "load" || method == "store"; - let ordering_arg = if method == "load" { &args[1] } else { &args[2] }; - if let ExprKind::Path(ref ordering_qpath) = ordering_arg.kind; - if let Some(ordering_def_id) = cx.qpath_res(ordering_qpath, ordering_arg.hir_id).opt_def_id(); - then { - if method == "load" && - match_ordering_def_path(cx, ordering_def_id, &["Release", "AcqRel"]) { - span_lint_and_help( - cx, - INVALID_ATOMIC_ORDERING, - ordering_arg.span, - "atomic loads cannot have `Release` and `AcqRel` ordering", - None, - "consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`" - ); - } else if method == "store" && - match_ordering_def_path(cx, ordering_def_id, &["Acquire", "AcqRel"]) { - span_lint_and_help( - cx, - INVALID_ATOMIC_ORDERING, - ordering_arg.span, - "atomic stores cannot have `Acquire` and `AcqRel` ordering", - None, - "consider using ordering modes `Release`, `SeqCst` or `Relaxed`" - ); - } - } - } -} - -fn check_memory_fence(cx: &LateContext<'_>, expr: &Expr<'_>) { - if_chain! { - if let ExprKind::Call(func, args) = expr.kind; - if let ExprKind::Path(ref func_qpath) = func.kind; - if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); - if ["fence", "compiler_fence"] - .iter() - .any(|func| match_def_path(cx, def_id, &["core", "sync", "atomic", func])); - if let ExprKind::Path(ref ordering_qpath) = &args[0].kind; - if let Some(ordering_def_id) = cx.qpath_res(ordering_qpath, args[0].hir_id).opt_def_id(); - if match_ordering_def_path(cx, ordering_def_id, &["Relaxed"]); - then { - span_lint_and_help( - cx, - INVALID_ATOMIC_ORDERING, - args[0].span, - "memory fences cannot have `Relaxed` ordering", - None, - "consider using ordering modes `Acquire`, `Release`, `AcqRel` or `SeqCst`" - ); - } - } -} - -fn opt_ordering_defid(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option { - if let ExprKind::Path(ref ord_qpath) = ord_arg.kind { - cx.qpath_res(ord_qpath, ord_arg.hir_id).opt_def_id() - } else { - None - } -} - -fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) { - if_chain! { - if let ExprKind::MethodCall(method_path, _, args, _) = &expr.kind; - let method = method_path.ident.name.as_str(); - if type_is_atomic(cx, &args[0]); - if method == "compare_exchange" || method == "compare_exchange_weak" || method == "fetch_update"; - let (success_order_arg, failure_order_arg) = if method == "fetch_update" { - (&args[1], &args[2]) - } else { - (&args[3], &args[4]) - }; - if let Some(fail_ordering_def_id) = opt_ordering_defid(cx, failure_order_arg); - then { - // Helper type holding on to some checking and error reporting data. Has - // - (success ordering name, - // - list of failure orderings forbidden by the success order, - // - suggestion message) - type OrdLintInfo = (&'static str, &'static [&'static str], &'static str); - let relaxed: OrdLintInfo = ("Relaxed", &["SeqCst", "Acquire"], "ordering mode `Relaxed`"); - let acquire: OrdLintInfo = ("Acquire", &["SeqCst"], "ordering modes `Acquire` or `Relaxed`"); - let seq_cst: OrdLintInfo = ("SeqCst", &[], "ordering modes `Acquire`, `SeqCst` or `Relaxed`"); - let release = ("Release", relaxed.1, relaxed.2); - let acqrel = ("AcqRel", acquire.1, acquire.2); - let search = [relaxed, acquire, seq_cst, release, acqrel]; - - let success_lint_info = opt_ordering_defid(cx, success_order_arg) - .and_then(|success_ord_def_id| -> Option { - search - .iter() - .find(|(ordering, ..)| { - match_def_path(cx, success_ord_def_id, - &["core", "sync", "atomic", "Ordering", ordering]) - }) - .copied() - }); - - if match_ordering_def_path(cx, fail_ordering_def_id, &["Release", "AcqRel"]) { - // If we don't know the success order is, use what we'd suggest - // if it were maximally permissive. - let suggested = success_lint_info.unwrap_or(seq_cst).2; - span_lint_and_help( - cx, - INVALID_ATOMIC_ORDERING, - failure_order_arg.span, - &format!( - "{}'s failure ordering may not be `Release` or `AcqRel`", - method, - ), - None, - &format!("consider using {} instead", suggested), - ); - } else if let Some((success_ord_name, bad_ords_given_success, suggested)) = success_lint_info { - if match_ordering_def_path(cx, fail_ordering_def_id, bad_ords_given_success) { - span_lint_and_help( - cx, - INVALID_ATOMIC_ORDERING, - failure_order_arg.span, - &format!( - "{}'s failure ordering may not be stronger than the success ordering of `{}`", - method, - success_ord_name, - ), - None, - &format!("consider using {} instead", suggested), - ); - } - } - } - } -} - -impl<'tcx> LateLintPass<'tcx> for AtomicOrdering { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - check_atomic_load_store(cx, expr); - check_memory_fence(cx, expr); - check_atomic_compare_exchange(cx, expr); - } -} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f49b382c5ea3b..e455c8db03f52 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -165,7 +165,6 @@ mod asm_syntax; mod assertions_on_constants; mod assign_ops; mod async_yields_async; -mod atomic_ordering; mod attrs; mod await_holding_invalid; mod bit_mask; @@ -537,7 +536,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: assign_ops::ASSIGN_OP_PATTERN, assign_ops::MISREFACTORED_ASSIGN_OP, async_yields_async::ASYNC_YIELDS_ASYNC, - atomic_ordering::INVALID_ATOMIC_ORDERING, attrs::BLANKET_CLIPPY_RESTRICTION_LINTS, attrs::DEPRECATED_CFG_ATTR, attrs::DEPRECATED_SEMVER, @@ -1174,7 +1172,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(assign_ops::ASSIGN_OP_PATTERN), LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP), LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC), - LintId::of(atomic_ordering::INVALID_ATOMIC_ORDERING), LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), LintId::of(attrs::DEPRECATED_CFG_ATTR), LintId::of(attrs::DEPRECATED_SEMVER), @@ -1670,7 +1667,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS), LintId::of(approx_const::APPROX_CONSTANT), LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC), - LintId::of(atomic_ordering::INVALID_ATOMIC_ORDERING), LintId::of(attrs::DEPRECATED_SEMVER), LintId::of(attrs::MISMATCHED_TARGET_OS), LintId::of(attrs::USELESS_ATTRIBUTE), @@ -2044,7 +2040,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box floating_point_arithmetic::FloatingPointArithmetic); store.register_early_pass(|| box as_conversions::AsConversions); store.register_late_pass(|| box let_underscore::LetUnderscore); - store.register_late_pass(|| box atomic_ordering::AtomicOrdering); store.register_early_pass(|| box single_component_path_imports::SingleComponentPathImports); let max_fn_params_bools = conf.max_fn_params_bools; let max_struct_bools = conf.max_struct_bools; @@ -2183,6 +2178,7 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { ls.register_renamed("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"); ls.register_renamed("clippy::panic_params", "non_fmt_panics"); ls.register_renamed("clippy::unknown_clippy_lints", "unknown_lints"); + ls.register_renamed("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"); } // only exists to let the dogfood integration test works. diff --git a/tests/ui/atomic_ordering_bool.rs b/tests/ui/atomic_ordering_bool.rs deleted file mode 100644 index cdbde79b19ebf..0000000000000 --- a/tests/ui/atomic_ordering_bool.rs +++ /dev/null @@ -1,25 +0,0 @@ -#![warn(clippy::invalid_atomic_ordering)] - -use std::sync::atomic::{AtomicBool, Ordering}; - -fn main() { - let x = AtomicBool::new(true); - - // Allowed load ordering modes - let _ = x.load(Ordering::Acquire); - let _ = x.load(Ordering::SeqCst); - let _ = x.load(Ordering::Relaxed); - - // Disallowed load ordering modes - let _ = x.load(Ordering::Release); - let _ = x.load(Ordering::AcqRel); - - // Allowed store ordering modes - x.store(false, Ordering::Release); - x.store(false, Ordering::SeqCst); - x.store(false, Ordering::Relaxed); - - // Disallowed store ordering modes - x.store(false, Ordering::Acquire); - x.store(false, Ordering::AcqRel); -} diff --git a/tests/ui/atomic_ordering_bool.stderr b/tests/ui/atomic_ordering_bool.stderr deleted file mode 100644 index 397b893aed964..0000000000000 --- a/tests/ui/atomic_ordering_bool.stderr +++ /dev/null @@ -1,35 +0,0 @@ -error: atomic loads cannot have `Release` and `AcqRel` ordering - --> $DIR/atomic_ordering_bool.rs:14:20 - | -LL | let _ = x.load(Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings` - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` - -error: atomic loads cannot have `Release` and `AcqRel` ordering - --> $DIR/atomic_ordering_bool.rs:15:20 - | -LL | let _ = x.load(Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` - -error: atomic stores cannot have `Acquire` and `AcqRel` ordering - --> $DIR/atomic_ordering_bool.rs:23:20 - | -LL | x.store(false, Ordering::Acquire); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` - -error: atomic stores cannot have `Acquire` and `AcqRel` ordering - --> $DIR/atomic_ordering_bool.rs:24:20 - | -LL | x.store(false, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` - -error: aborting due to 4 previous errors - diff --git a/tests/ui/atomic_ordering_exchange.rs b/tests/ui/atomic_ordering_exchange.rs deleted file mode 100644 index 1ddc12f9ab213..0000000000000 --- a/tests/ui/atomic_ordering_exchange.rs +++ /dev/null @@ -1,45 +0,0 @@ -#![warn(clippy::invalid_atomic_ordering)] - -use std::sync::atomic::{AtomicUsize, Ordering}; - -fn main() { - // `compare_exchange` (not weak) testing - let x = AtomicUsize::new(0); - - // Allowed ordering combos - let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Relaxed); - let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::Acquire); - let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::Relaxed); - let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Relaxed); - let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::Acquire); - let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::Relaxed); - let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Relaxed); - let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Acquire); - let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::SeqCst); - - // AcqRel is always forbidden as a failure ordering - let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::AcqRel); - let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::AcqRel); - let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::AcqRel); - let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::AcqRel); - let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::AcqRel); - - // Release is always forbidden as a failure ordering - let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Release); - let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::Release); - let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Release); - let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::Release); - let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Release); - - // Release success order forbids failure order of Acquire or SeqCst - let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Acquire); - let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::SeqCst); - - // Relaxed success order also forbids failure order of Acquire or SeqCst - let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::SeqCst); - let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Acquire); - - // Acquire/AcqRel forbids failure order of SeqCst - let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::SeqCst); - let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::SeqCst); -} diff --git a/tests/ui/atomic_ordering_exchange.stderr b/tests/ui/atomic_ordering_exchange.stderr deleted file mode 100644 index 4b9bfef79748c..0000000000000 --- a/tests/ui/atomic_ordering_exchange.stderr +++ /dev/null @@ -1,131 +0,0 @@ -error: compare_exchange's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:21:57 - | -LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings` - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:22:57 - | -LL | let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire` or `Relaxed` instead - -error: compare_exchange's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:23:57 - | -LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:24:56 - | -LL | let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire` or `Relaxed` instead - -error: compare_exchange's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:25:56 - | -LL | let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead - -error: compare_exchange's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:28:57 - | -LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:29:57 - | -LL | let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire` or `Relaxed` instead - -error: compare_exchange's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:30:57 - | -LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:31:56 - | -LL | let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire` or `Relaxed` instead - -error: compare_exchange's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:32:56 - | -LL | let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead - -error: compare_exchange's failure ordering may not be stronger than the success ordering of `Release` - --> $DIR/atomic_ordering_exchange.rs:35:57 - | -LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Acquire); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange's failure ordering may not be stronger than the success ordering of `Release` - --> $DIR/atomic_ordering_exchange.rs:36:57 - | -LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::SeqCst); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange's failure ordering may not be stronger than the success ordering of `Relaxed` - --> $DIR/atomic_ordering_exchange.rs:39:57 - | -LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::SeqCst); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange's failure ordering may not be stronger than the success ordering of `Relaxed` - --> $DIR/atomic_ordering_exchange.rs:40:57 - | -LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Acquire); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange's failure ordering may not be stronger than the success ordering of `Acquire` - --> $DIR/atomic_ordering_exchange.rs:43:57 - | -LL | let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::SeqCst); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire` or `Relaxed` instead - -error: compare_exchange's failure ordering may not be stronger than the success ordering of `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:44:56 - | -LL | let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::SeqCst); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire` or `Relaxed` instead - -error: aborting due to 16 previous errors - diff --git a/tests/ui/atomic_ordering_exchange_weak.rs b/tests/ui/atomic_ordering_exchange_weak.rs deleted file mode 100644 index 5906990250728..0000000000000 --- a/tests/ui/atomic_ordering_exchange_weak.rs +++ /dev/null @@ -1,47 +0,0 @@ -#![warn(clippy::invalid_atomic_ordering)] - -use std::sync::atomic::{AtomicPtr, Ordering}; - -fn main() { - let ptr = &mut 5; - let ptr2 = &mut 10; - // `compare_exchange_weak` testing - let x = AtomicPtr::new(ptr); - - // Allowed ordering combos - let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Relaxed); - let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Acquire, Ordering::Acquire); - let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Acquire, Ordering::Relaxed); - let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Release, Ordering::Relaxed); - let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::AcqRel, Ordering::Acquire); - let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::AcqRel, Ordering::Relaxed); - let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::Relaxed); - let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::Acquire); - let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::SeqCst); - - // AcqRel is always forbidden as a failure ordering - let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Relaxed, Ordering::AcqRel); - let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering::AcqRel); - let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::AcqRel); - let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering::AcqRel); - let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::SeqCst, Ordering::AcqRel); - - // Release is always forbidden as a failure ordering - let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Release); - let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Acquire, Ordering::Release); - let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Release, Ordering::Release); - let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::AcqRel, Ordering::Release); - let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::Release); - - // Release success order forbids failure order of Acquire or SeqCst - let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::Acquire); - let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::SeqCst); - - // Relaxed success order also forbids failure order of Acquire or SeqCst - let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::SeqCst); - let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Acquire); - - // Acquire/AcqRel forbids failure order of SeqCst - let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering::SeqCst); - let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering::SeqCst); -} diff --git a/tests/ui/atomic_ordering_exchange_weak.stderr b/tests/ui/atomic_ordering_exchange_weak.stderr deleted file mode 100644 index de7026f3ffafa..0000000000000 --- a/tests/ui/atomic_ordering_exchange_weak.stderr +++ /dev/null @@ -1,131 +0,0 @@ -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange_weak.rs:23:67 - | -LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Relaxed, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings` - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange_weak.rs:24:67 - | -LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire` or `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange_weak.rs:25:67 - | -LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange_weak.rs:26:66 - | -LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire` or `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange_weak.rs:27:66 - | -LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::SeqCst, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange_weak.rs:30:67 - | -LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange_weak.rs:31:67 - | -LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Acquire, Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire` or `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange_weak.rs:32:67 - | -LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Release, Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange_weak.rs:33:66 - | -LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::AcqRel, Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire` or `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange_weak.rs:34:66 - | -LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Release` - --> $DIR/atomic_ordering_exchange_weak.rs:37:67 - | -LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::Acquire); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Release` - --> $DIR/atomic_ordering_exchange_weak.rs:38:67 - | -LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::SeqCst); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Relaxed` - --> $DIR/atomic_ordering_exchange_weak.rs:41:67 - | -LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::SeqCst); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Relaxed` - --> $DIR/atomic_ordering_exchange_weak.rs:42:67 - | -LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Acquire); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Acquire` - --> $DIR/atomic_ordering_exchange_weak.rs:45:67 - | -LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering::SeqCst); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire` or `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `AcqRel` - --> $DIR/atomic_ordering_exchange_weak.rs:46:66 - | -LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering::SeqCst); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire` or `Relaxed` instead - -error: aborting due to 16 previous errors - diff --git a/tests/ui/atomic_ordering_fence.rs b/tests/ui/atomic_ordering_fence.rs deleted file mode 100644 index 5ee5182ca051a..0000000000000 --- a/tests/ui/atomic_ordering_fence.rs +++ /dev/null @@ -1,20 +0,0 @@ -#![warn(clippy::invalid_atomic_ordering)] - -use std::sync::atomic::{compiler_fence, fence, Ordering}; - -fn main() { - // Allowed fence ordering modes - fence(Ordering::Acquire); - fence(Ordering::Release); - fence(Ordering::AcqRel); - fence(Ordering::SeqCst); - - // Disallowed fence ordering modes - fence(Ordering::Relaxed); - - compiler_fence(Ordering::Acquire); - compiler_fence(Ordering::Release); - compiler_fence(Ordering::AcqRel); - compiler_fence(Ordering::SeqCst); - compiler_fence(Ordering::Relaxed); -} diff --git a/tests/ui/atomic_ordering_fence.stderr b/tests/ui/atomic_ordering_fence.stderr deleted file mode 100644 index 3ceff27d9ad5e..0000000000000 --- a/tests/ui/atomic_ordering_fence.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error: memory fences cannot have `Relaxed` ordering - --> $DIR/atomic_ordering_fence.rs:13:11 - | -LL | fence(Ordering::Relaxed); - | ^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings` - = help: consider using ordering modes `Acquire`, `Release`, `AcqRel` or `SeqCst` - -error: memory fences cannot have `Relaxed` ordering - --> $DIR/atomic_ordering_fence.rs:19:20 - | -LL | compiler_fence(Ordering::Relaxed); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire`, `Release`, `AcqRel` or `SeqCst` - -error: aborting due to 2 previous errors - diff --git a/tests/ui/atomic_ordering_fetch_update.rs b/tests/ui/atomic_ordering_fetch_update.rs deleted file mode 100644 index 550bdb001e4cd..0000000000000 --- a/tests/ui/atomic_ordering_fetch_update.rs +++ /dev/null @@ -1,45 +0,0 @@ -#![warn(clippy::invalid_atomic_ordering)] - -use std::sync::atomic::{AtomicIsize, Ordering}; - -fn main() { - // `fetch_update` testing - let x = AtomicIsize::new(0); - - // Allowed ordering combos - let _ = x.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |old| Some(old + 1)); - let _ = x.fetch_update(Ordering::Acquire, Ordering::Acquire, |old| Some(old + 1)); - let _ = x.fetch_update(Ordering::Acquire, Ordering::Relaxed, |old| Some(old + 1)); - let _ = x.fetch_update(Ordering::Release, Ordering::Relaxed, |old| Some(old + 1)); - let _ = x.fetch_update(Ordering::AcqRel, Ordering::Acquire, |old| Some(old + 1)); - let _ = x.fetch_update(Ordering::AcqRel, Ordering::Relaxed, |old| Some(old + 1)); - let _ = x.fetch_update(Ordering::SeqCst, Ordering::Relaxed, |old| Some(old + 1)); - let _ = x.fetch_update(Ordering::SeqCst, Ordering::Acquire, |old| Some(old + 1)); - let _ = x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |old| Some(old + 1)); - - // AcqRel is always forbidden as a failure ordering - let _ = x.fetch_update(Ordering::Relaxed, Ordering::AcqRel, |old| Some(old + 1)); - let _ = x.fetch_update(Ordering::Acquire, Ordering::AcqRel, |old| Some(old + 1)); - let _ = x.fetch_update(Ordering::Release, Ordering::AcqRel, |old| Some(old + 1)); - let _ = x.fetch_update(Ordering::AcqRel, Ordering::AcqRel, |old| Some(old + 1)); - let _ = x.fetch_update(Ordering::SeqCst, Ordering::AcqRel, |old| Some(old + 1)); - - // Release is always forbidden as a failure ordering - let _ = x.fetch_update(Ordering::Relaxed, Ordering::Release, |old| Some(old + 1)); - let _ = x.fetch_update(Ordering::Acquire, Ordering::Release, |old| Some(old + 1)); - let _ = x.fetch_update(Ordering::Release, Ordering::Release, |old| Some(old + 1)); - let _ = x.fetch_update(Ordering::AcqRel, Ordering::Release, |old| Some(old + 1)); - let _ = x.fetch_update(Ordering::SeqCst, Ordering::Release, |old| Some(old + 1)); - - // Release success order forbids failure order of Acquire or SeqCst - let _ = x.fetch_update(Ordering::Release, Ordering::Acquire, |old| Some(old + 1)); - let _ = x.fetch_update(Ordering::Release, Ordering::SeqCst, |old| Some(old + 1)); - - // Relaxed success order also forbids failure order of Acquire or SeqCst - let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |old| Some(old + 1)); - let _ = x.fetch_update(Ordering::Relaxed, Ordering::Acquire, |old| Some(old + 1)); - - // Acquire/AcqRel forbids failure order of SeqCst - let _ = x.fetch_update(Ordering::Acquire, Ordering::SeqCst, |old| Some(old + 1)); - let _ = x.fetch_update(Ordering::AcqRel, Ordering::SeqCst, |old| Some(old + 1)); -} diff --git a/tests/ui/atomic_ordering_fetch_update.stderr b/tests/ui/atomic_ordering_fetch_update.stderr deleted file mode 100644 index 694548ece97b2..0000000000000 --- a/tests/ui/atomic_ordering_fetch_update.stderr +++ /dev/null @@ -1,131 +0,0 @@ -error: fetch_update's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_fetch_update.rs:21:47 - | -LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::AcqRel, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings` - = help: consider using ordering mode `Relaxed` instead - -error: fetch_update's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_fetch_update.rs:22:47 - | -LL | let _ = x.fetch_update(Ordering::Acquire, Ordering::AcqRel, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire` or `Relaxed` instead - -error: fetch_update's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_fetch_update.rs:23:47 - | -LL | let _ = x.fetch_update(Ordering::Release, Ordering::AcqRel, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: fetch_update's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_fetch_update.rs:24:46 - | -LL | let _ = x.fetch_update(Ordering::AcqRel, Ordering::AcqRel, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire` or `Relaxed` instead - -error: fetch_update's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_fetch_update.rs:25:46 - | -LL | let _ = x.fetch_update(Ordering::SeqCst, Ordering::AcqRel, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead - -error: fetch_update's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_fetch_update.rs:28:47 - | -LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::Release, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: fetch_update's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_fetch_update.rs:29:47 - | -LL | let _ = x.fetch_update(Ordering::Acquire, Ordering::Release, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire` or `Relaxed` instead - -error: fetch_update's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_fetch_update.rs:30:47 - | -LL | let _ = x.fetch_update(Ordering::Release, Ordering::Release, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: fetch_update's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_fetch_update.rs:31:46 - | -LL | let _ = x.fetch_update(Ordering::AcqRel, Ordering::Release, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire` or `Relaxed` instead - -error: fetch_update's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_fetch_update.rs:32:46 - | -LL | let _ = x.fetch_update(Ordering::SeqCst, Ordering::Release, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead - -error: fetch_update's failure ordering may not be stronger than the success ordering of `Release` - --> $DIR/atomic_ordering_fetch_update.rs:35:47 - | -LL | let _ = x.fetch_update(Ordering::Release, Ordering::Acquire, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: fetch_update's failure ordering may not be stronger than the success ordering of `Release` - --> $DIR/atomic_ordering_fetch_update.rs:36:47 - | -LL | let _ = x.fetch_update(Ordering::Release, Ordering::SeqCst, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: fetch_update's failure ordering may not be stronger than the success ordering of `Relaxed` - --> $DIR/atomic_ordering_fetch_update.rs:39:47 - | -LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: fetch_update's failure ordering may not be stronger than the success ordering of `Relaxed` - --> $DIR/atomic_ordering_fetch_update.rs:40:47 - | -LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::Acquire, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: fetch_update's failure ordering may not be stronger than the success ordering of `Acquire` - --> $DIR/atomic_ordering_fetch_update.rs:43:47 - | -LL | let _ = x.fetch_update(Ordering::Acquire, Ordering::SeqCst, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire` or `Relaxed` instead - -error: fetch_update's failure ordering may not be stronger than the success ordering of `AcqRel` - --> $DIR/atomic_ordering_fetch_update.rs:44:46 - | -LL | let _ = x.fetch_update(Ordering::AcqRel, Ordering::SeqCst, |old| Some(old + 1)); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire` or `Relaxed` instead - -error: aborting due to 16 previous errors - diff --git a/tests/ui/atomic_ordering_int.rs b/tests/ui/atomic_ordering_int.rs deleted file mode 100644 index 40a00ba3de350..0000000000000 --- a/tests/ui/atomic_ordering_int.rs +++ /dev/null @@ -1,86 +0,0 @@ -#![warn(clippy::invalid_atomic_ordering)] - -use std::sync::atomic::{AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, Ordering}; - -fn main() { - // `AtomicI8` test cases - let x = AtomicI8::new(0); - - // Allowed load ordering modes - let _ = x.load(Ordering::Acquire); - let _ = x.load(Ordering::SeqCst); - let _ = x.load(Ordering::Relaxed); - - // Disallowed load ordering modes - let _ = x.load(Ordering::Release); - let _ = x.load(Ordering::AcqRel); - - // Allowed store ordering modes - x.store(1, Ordering::Release); - x.store(1, Ordering::SeqCst); - x.store(1, Ordering::Relaxed); - - // Disallowed store ordering modes - x.store(1, Ordering::Acquire); - x.store(1, Ordering::AcqRel); - - // `AtomicI16` test cases - let x = AtomicI16::new(0); - - let _ = x.load(Ordering::Acquire); - let _ = x.load(Ordering::SeqCst); - let _ = x.load(Ordering::Relaxed); - let _ = x.load(Ordering::Release); - let _ = x.load(Ordering::AcqRel); - - x.store(1, Ordering::Release); - x.store(1, Ordering::SeqCst); - x.store(1, Ordering::Relaxed); - x.store(1, Ordering::Acquire); - x.store(1, Ordering::AcqRel); - - // `AtomicI32` test cases - let x = AtomicI32::new(0); - - let _ = x.load(Ordering::Acquire); - let _ = x.load(Ordering::SeqCst); - let _ = x.load(Ordering::Relaxed); - let _ = x.load(Ordering::Release); - let _ = x.load(Ordering::AcqRel); - - x.store(1, Ordering::Release); - x.store(1, Ordering::SeqCst); - x.store(1, Ordering::Relaxed); - x.store(1, Ordering::Acquire); - x.store(1, Ordering::AcqRel); - - // `AtomicI64` test cases - let x = AtomicI64::new(0); - - let _ = x.load(Ordering::Acquire); - let _ = x.load(Ordering::SeqCst); - let _ = x.load(Ordering::Relaxed); - let _ = x.load(Ordering::Release); - let _ = x.load(Ordering::AcqRel); - - x.store(1, Ordering::Release); - x.store(1, Ordering::SeqCst); - x.store(1, Ordering::Relaxed); - x.store(1, Ordering::Acquire); - x.store(1, Ordering::AcqRel); - - // `AtomicIsize` test cases - let x = AtomicIsize::new(0); - - let _ = x.load(Ordering::Acquire); - let _ = x.load(Ordering::SeqCst); - let _ = x.load(Ordering::Relaxed); - let _ = x.load(Ordering::Release); - let _ = x.load(Ordering::AcqRel); - - x.store(1, Ordering::Release); - x.store(1, Ordering::SeqCst); - x.store(1, Ordering::Relaxed); - x.store(1, Ordering::Acquire); - x.store(1, Ordering::AcqRel); -} diff --git a/tests/ui/atomic_ordering_int.stderr b/tests/ui/atomic_ordering_int.stderr deleted file mode 100644 index bbaf234d3c9f8..0000000000000 --- a/tests/ui/atomic_ordering_int.stderr +++ /dev/null @@ -1,163 +0,0 @@ -error: atomic loads cannot have `Release` and `AcqRel` ordering - --> $DIR/atomic_ordering_int.rs:15:20 - | -LL | let _ = x.load(Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings` - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` - -error: atomic loads cannot have `Release` and `AcqRel` ordering - --> $DIR/atomic_ordering_int.rs:16:20 - | -LL | let _ = x.load(Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` - -error: atomic stores cannot have `Acquire` and `AcqRel` ordering - --> $DIR/atomic_ordering_int.rs:24:16 - | -LL | x.store(1, Ordering::Acquire); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` - -error: atomic stores cannot have `Acquire` and `AcqRel` ordering - --> $DIR/atomic_ordering_int.rs:25:16 - | -LL | x.store(1, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` - -error: atomic loads cannot have `Release` and `AcqRel` ordering - --> $DIR/atomic_ordering_int.rs:33:20 - | -LL | let _ = x.load(Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` - -error: atomic loads cannot have `Release` and `AcqRel` ordering - --> $DIR/atomic_ordering_int.rs:34:20 - | -LL | let _ = x.load(Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` - -error: atomic stores cannot have `Acquire` and `AcqRel` ordering - --> $DIR/atomic_ordering_int.rs:39:16 - | -LL | x.store(1, Ordering::Acquire); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` - -error: atomic stores cannot have `Acquire` and `AcqRel` ordering - --> $DIR/atomic_ordering_int.rs:40:16 - | -LL | x.store(1, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` - -error: atomic loads cannot have `Release` and `AcqRel` ordering - --> $DIR/atomic_ordering_int.rs:48:20 - | -LL | let _ = x.load(Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` - -error: atomic loads cannot have `Release` and `AcqRel` ordering - --> $DIR/atomic_ordering_int.rs:49:20 - | -LL | let _ = x.load(Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` - -error: atomic stores cannot have `Acquire` and `AcqRel` ordering - --> $DIR/atomic_ordering_int.rs:54:16 - | -LL | x.store(1, Ordering::Acquire); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` - -error: atomic stores cannot have `Acquire` and `AcqRel` ordering - --> $DIR/atomic_ordering_int.rs:55:16 - | -LL | x.store(1, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` - -error: atomic loads cannot have `Release` and `AcqRel` ordering - --> $DIR/atomic_ordering_int.rs:63:20 - | -LL | let _ = x.load(Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` - -error: atomic loads cannot have `Release` and `AcqRel` ordering - --> $DIR/atomic_ordering_int.rs:64:20 - | -LL | let _ = x.load(Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` - -error: atomic stores cannot have `Acquire` and `AcqRel` ordering - --> $DIR/atomic_ordering_int.rs:69:16 - | -LL | x.store(1, Ordering::Acquire); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` - -error: atomic stores cannot have `Acquire` and `AcqRel` ordering - --> $DIR/atomic_ordering_int.rs:70:16 - | -LL | x.store(1, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` - -error: atomic loads cannot have `Release` and `AcqRel` ordering - --> $DIR/atomic_ordering_int.rs:78:20 - | -LL | let _ = x.load(Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` - -error: atomic loads cannot have `Release` and `AcqRel` ordering - --> $DIR/atomic_ordering_int.rs:79:20 - | -LL | let _ = x.load(Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` - -error: atomic stores cannot have `Acquire` and `AcqRel` ordering - --> $DIR/atomic_ordering_int.rs:84:16 - | -LL | x.store(1, Ordering::Acquire); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` - -error: atomic stores cannot have `Acquire` and `AcqRel` ordering - --> $DIR/atomic_ordering_int.rs:85:16 - | -LL | x.store(1, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` - -error: aborting due to 20 previous errors - diff --git a/tests/ui/atomic_ordering_ptr.rs b/tests/ui/atomic_ordering_ptr.rs deleted file mode 100644 index ecbb05c7fbc39..0000000000000 --- a/tests/ui/atomic_ordering_ptr.rs +++ /dev/null @@ -1,27 +0,0 @@ -#![warn(clippy::invalid_atomic_ordering)] - -use std::sync::atomic::{AtomicPtr, Ordering}; - -fn main() { - let ptr = &mut 5; - let other_ptr = &mut 10; - let x = AtomicPtr::new(ptr); - - // Allowed load ordering modes - let _ = x.load(Ordering::Acquire); - let _ = x.load(Ordering::SeqCst); - let _ = x.load(Ordering::Relaxed); - - // Disallowed load ordering modes - let _ = x.load(Ordering::Release); - let _ = x.load(Ordering::AcqRel); - - // Allowed store ordering modes - x.store(other_ptr, Ordering::Release); - x.store(other_ptr, Ordering::SeqCst); - x.store(other_ptr, Ordering::Relaxed); - - // Disallowed store ordering modes - x.store(other_ptr, Ordering::Acquire); - x.store(other_ptr, Ordering::AcqRel); -} diff --git a/tests/ui/atomic_ordering_ptr.stderr b/tests/ui/atomic_ordering_ptr.stderr deleted file mode 100644 index 558ae55518d5a..0000000000000 --- a/tests/ui/atomic_ordering_ptr.stderr +++ /dev/null @@ -1,35 +0,0 @@ -error: atomic loads cannot have `Release` and `AcqRel` ordering - --> $DIR/atomic_ordering_ptr.rs:16:20 - | -LL | let _ = x.load(Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings` - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` - -error: atomic loads cannot have `Release` and `AcqRel` ordering - --> $DIR/atomic_ordering_ptr.rs:17:20 - | -LL | let _ = x.load(Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` - -error: atomic stores cannot have `Acquire` and `AcqRel` ordering - --> $DIR/atomic_ordering_ptr.rs:25:24 - | -LL | x.store(other_ptr, Ordering::Acquire); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` - -error: atomic stores cannot have `Acquire` and `AcqRel` ordering - --> $DIR/atomic_ordering_ptr.rs:26:24 - | -LL | x.store(other_ptr, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` - -error: aborting due to 4 previous errors - diff --git a/tests/ui/atomic_ordering_uint.rs b/tests/ui/atomic_ordering_uint.rs deleted file mode 100644 index a0d5d7c401035..0000000000000 --- a/tests/ui/atomic_ordering_uint.rs +++ /dev/null @@ -1,86 +0,0 @@ -#![warn(clippy::invalid_atomic_ordering)] - -use std::sync::atomic::{AtomicU16, AtomicU32, AtomicU64, AtomicU8, AtomicUsize, Ordering}; - -fn main() { - // `AtomicU8` test cases - let x = AtomicU8::new(0); - - // Allowed load ordering modes - let _ = x.load(Ordering::Acquire); - let _ = x.load(Ordering::SeqCst); - let _ = x.load(Ordering::Relaxed); - - // Disallowed load ordering modes - let _ = x.load(Ordering::Release); - let _ = x.load(Ordering::AcqRel); - - // Allowed store ordering modes - x.store(1, Ordering::Release); - x.store(1, Ordering::SeqCst); - x.store(1, Ordering::Relaxed); - - // Disallowed store ordering modes - x.store(1, Ordering::Acquire); - x.store(1, Ordering::AcqRel); - - // `AtomicU16` test cases - let x = AtomicU16::new(0); - - let _ = x.load(Ordering::Acquire); - let _ = x.load(Ordering::SeqCst); - let _ = x.load(Ordering::Relaxed); - let _ = x.load(Ordering::Release); - let _ = x.load(Ordering::AcqRel); - - x.store(1, Ordering::Release); - x.store(1, Ordering::SeqCst); - x.store(1, Ordering::Relaxed); - x.store(1, Ordering::Acquire); - x.store(1, Ordering::AcqRel); - - // `AtomicU32` test cases - let x = AtomicU32::new(0); - - let _ = x.load(Ordering::Acquire); - let _ = x.load(Ordering::SeqCst); - let _ = x.load(Ordering::Relaxed); - let _ = x.load(Ordering::Release); - let _ = x.load(Ordering::AcqRel); - - x.store(1, Ordering::Release); - x.store(1, Ordering::SeqCst); - x.store(1, Ordering::Relaxed); - x.store(1, Ordering::Acquire); - x.store(1, Ordering::AcqRel); - - // `AtomicU64` test cases - let x = AtomicU64::new(0); - - let _ = x.load(Ordering::Acquire); - let _ = x.load(Ordering::SeqCst); - let _ = x.load(Ordering::Relaxed); - let _ = x.load(Ordering::Release); - let _ = x.load(Ordering::AcqRel); - - x.store(1, Ordering::Release); - x.store(1, Ordering::SeqCst); - x.store(1, Ordering::Relaxed); - x.store(1, Ordering::Acquire); - x.store(1, Ordering::AcqRel); - - // `AtomicUsize` test cases - let x = AtomicUsize::new(0); - - let _ = x.load(Ordering::Acquire); - let _ = x.load(Ordering::SeqCst); - let _ = x.load(Ordering::Relaxed); - let _ = x.load(Ordering::Release); - let _ = x.load(Ordering::AcqRel); - - x.store(1, Ordering::Release); - x.store(1, Ordering::SeqCst); - x.store(1, Ordering::Relaxed); - x.store(1, Ordering::Acquire); - x.store(1, Ordering::AcqRel); -} diff --git a/tests/ui/atomic_ordering_uint.stderr b/tests/ui/atomic_ordering_uint.stderr deleted file mode 100644 index 5703135bcf1e2..0000000000000 --- a/tests/ui/atomic_ordering_uint.stderr +++ /dev/null @@ -1,163 +0,0 @@ -error: atomic loads cannot have `Release` and `AcqRel` ordering - --> $DIR/atomic_ordering_uint.rs:15:20 - | -LL | let _ = x.load(Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings` - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` - -error: atomic loads cannot have `Release` and `AcqRel` ordering - --> $DIR/atomic_ordering_uint.rs:16:20 - | -LL | let _ = x.load(Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` - -error: atomic stores cannot have `Acquire` and `AcqRel` ordering - --> $DIR/atomic_ordering_uint.rs:24:16 - | -LL | x.store(1, Ordering::Acquire); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` - -error: atomic stores cannot have `Acquire` and `AcqRel` ordering - --> $DIR/atomic_ordering_uint.rs:25:16 - | -LL | x.store(1, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` - -error: atomic loads cannot have `Release` and `AcqRel` ordering - --> $DIR/atomic_ordering_uint.rs:33:20 - | -LL | let _ = x.load(Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` - -error: atomic loads cannot have `Release` and `AcqRel` ordering - --> $DIR/atomic_ordering_uint.rs:34:20 - | -LL | let _ = x.load(Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` - -error: atomic stores cannot have `Acquire` and `AcqRel` ordering - --> $DIR/atomic_ordering_uint.rs:39:16 - | -LL | x.store(1, Ordering::Acquire); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` - -error: atomic stores cannot have `Acquire` and `AcqRel` ordering - --> $DIR/atomic_ordering_uint.rs:40:16 - | -LL | x.store(1, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` - -error: atomic loads cannot have `Release` and `AcqRel` ordering - --> $DIR/atomic_ordering_uint.rs:48:20 - | -LL | let _ = x.load(Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` - -error: atomic loads cannot have `Release` and `AcqRel` ordering - --> $DIR/atomic_ordering_uint.rs:49:20 - | -LL | let _ = x.load(Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` - -error: atomic stores cannot have `Acquire` and `AcqRel` ordering - --> $DIR/atomic_ordering_uint.rs:54:16 - | -LL | x.store(1, Ordering::Acquire); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` - -error: atomic stores cannot have `Acquire` and `AcqRel` ordering - --> $DIR/atomic_ordering_uint.rs:55:16 - | -LL | x.store(1, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` - -error: atomic loads cannot have `Release` and `AcqRel` ordering - --> $DIR/atomic_ordering_uint.rs:63:20 - | -LL | let _ = x.load(Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` - -error: atomic loads cannot have `Release` and `AcqRel` ordering - --> $DIR/atomic_ordering_uint.rs:64:20 - | -LL | let _ = x.load(Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` - -error: atomic stores cannot have `Acquire` and `AcqRel` ordering - --> $DIR/atomic_ordering_uint.rs:69:16 - | -LL | x.store(1, Ordering::Acquire); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` - -error: atomic stores cannot have `Acquire` and `AcqRel` ordering - --> $DIR/atomic_ordering_uint.rs:70:16 - | -LL | x.store(1, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` - -error: atomic loads cannot have `Release` and `AcqRel` ordering - --> $DIR/atomic_ordering_uint.rs:78:20 - | -LL | let _ = x.load(Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` - -error: atomic loads cannot have `Release` and `AcqRel` ordering - --> $DIR/atomic_ordering_uint.rs:79:20 - | -LL | let _ = x.load(Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` - -error: atomic stores cannot have `Acquire` and `AcqRel` ordering - --> $DIR/atomic_ordering_uint.rs:84:16 - | -LL | x.store(1, Ordering::Acquire); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` - -error: atomic stores cannot have `Acquire` and `AcqRel` ordering - --> $DIR/atomic_ordering_uint.rs:85:16 - | -LL | x.store(1, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` - -error: aborting due to 20 previous errors - diff --git a/tests/ui/deprecated.rs b/tests/ui/deprecated.rs index 4ba9f0c1fcfff..1943d0092e624 100644 --- a/tests/ui/deprecated.rs +++ b/tests/ui/deprecated.rs @@ -14,5 +14,6 @@ #[warn(clippy::filter_map)] #[warn(clippy::pub_enum_variant_names)] #[warn(clippy::wrong_pub_self_convention)] +#[warn(clippy::invalid_atomic_ordering)] fn main() {} diff --git a/tests/ui/deprecated.stderr b/tests/ui/deprecated.stderr index c0002e5354310..51048e45c0677 100644 --- a/tests/ui/deprecated.stderr +++ b/tests/ui/deprecated.stderr @@ -96,5 +96,11 @@ error: lint `clippy::wrong_pub_self_convention` has been removed: set the `avoid LL | #[warn(clippy::wrong_pub_self_convention)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 16 previous errors +error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` + --> $DIR/deprecated.rs:17:8 + | +LL | #[warn(clippy::invalid_atomic_ordering)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` + +error: aborting due to 17 previous errors From 711c5cb6d2bd88dffff5c4da378d5796095c0cd2 Mon Sep 17 00:00:00 2001 From: dswij Date: Sat, 14 Aug 2021 16:59:08 +0800 Subject: [PATCH 20/94] Add false positive test for `manual_flatten` Add a scenario where `manual_flatten` is triggered when match expression will still be used after the match in `if let`. --- tests/ui/manual_flatten.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/ui/manual_flatten.rs b/tests/ui/manual_flatten.rs index b5bd35a68785a..7db6b730963c9 100644 --- a/tests/ui/manual_flatten.rs +++ b/tests/ui/manual_flatten.rs @@ -91,6 +91,19 @@ fn main() { } } + struct Test { + a: usize, + } + + let mut vec_of_struct = [Some(Test { a: 1 }), None]; + + // Usage of `if let` expression should not trigger lint + for n in vec_of_struct.iter_mut() { + if let Some(z) = n { + *n = None; + } + } + // Using manual flatten should not trigger the lint for n in vec![Some(1), Some(2), Some(3)].iter().flatten() { println!("{}", n); From a09bc1b6fe97839b23122ae7e386f75a1bf9c823 Mon Sep 17 00:00:00 2001 From: dswij Date: Sat, 14 Aug 2021 17:43:28 +0800 Subject: [PATCH 21/94] Check `if let` expr usage in `manual_flatten` `manual_flatten` should not trigger when match expression in `if let` is going to be used. --- clippy_lints/src/loops/manual_flatten.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/loops/manual_flatten.rs b/clippy_lints/src/loops/manual_flatten.rs index 64ff7574f86b7..d8153abc9653b 100644 --- a/clippy_lints/src/loops/manual_flatten.rs +++ b/clippy_lints/src/loops/manual_flatten.rs @@ -1,6 +1,7 @@ use super::utils::make_iterator_snippet; use super::MANUAL_FLATTEN; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::visitors::LocalUsedVisitor; use clippy_utils::{is_lang_ctor, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -37,16 +38,18 @@ pub(super) fn check<'tcx>( if_chain! { if let Some(inner_expr) = inner_expr; if let ExprKind::Match( - match_expr, match_arms, MatchSource::IfLetDesugar{ contains_else_clause: false } + match_expr, [true_arm, _else_arm], MatchSource::IfLetDesugar{ contains_else_clause: false } ) = inner_expr.kind; // Ensure match_expr in `if let` statement is the same as the pat from the for-loop if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind; if path_to_local_id(match_expr, pat_hir_id); // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result` - if let PatKind::TupleStruct(ref qpath, _, _) = match_arms[0].pat.kind; + if let PatKind::TupleStruct(ref qpath, _, _) = true_arm.pat.kind; let some_ctor = is_lang_ctor(cx, qpath, OptionSome); let ok_ctor = is_lang_ctor(cx, qpath, ResultOk); if some_ctor || ok_ctor; + // Ensure epxr in `if let` is not used afterwards + if !LocalUsedVisitor::new(cx, pat_hir_id).check_arm(true_arm); then { let if_let_type = if some_ctor { "Some" } else { "Ok" }; // Prepare the error message From 8cf6dae0ca66525fcdf519b588896e6cb5576c71 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 6 Jun 2021 20:01:14 -0400 Subject: [PATCH 22/94] Add `for_each_local_usage`. Switch `LocalUsedVisitor` to a function. --- clippy_lints/src/bytecount.rs | 4 +- clippy_lints/src/collapsible_match.rs | 7 +- clippy_lints/src/let_if_seq.rs | 21 ++-- clippy_lints/src/loops/for_kv_map.rs | 6 +- clippy_lints/src/loops/manual_flatten.rs | 4 +- clippy_lints/src/loops/needless_range_loop.rs | 25 ++-- clippy_lints/src/matches.rs | 6 +- clippy_lints/src/unused_self.rs | 5 +- clippy_utils/src/lib.rs | 1 + clippy_utils/src/visitors.rs | 116 +++++++++--------- 10 files changed, 91 insertions(+), 104 deletions(-) diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs index c444984bc133a..a07cd5e5f4e53 100644 --- a/clippy_lints/src/bytecount.rs +++ b/clippy_lints/src/bytecount.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::match_type; -use clippy_utils::visitors::LocalUsedVisitor; +use clippy_utils::visitors::is_local_used; use clippy_utils::{path_to_local_id, paths, peel_ref_operators, remove_blocks, strip_pat_refs}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -65,7 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount { return; }; if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind(); - if !LocalUsedVisitor::new(cx, arg_id).check_expr(needle); + if !is_local_used(cx, needle, arg_id); then { let haystack = if let ExprKind::MethodCall(path, _, args, _) = filter_recv.kind { diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/collapsible_match.rs index a403a9846babd..bd8f9cc7343bf 100644 --- a/clippy_lints/src/collapsible_match.rs +++ b/clippy_lints/src/collapsible_match.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::visitors::LocalUsedVisitor; +use clippy_utils::visitors::is_local_used; use clippy_utils::{is_lang_ctor, path_to_local, peel_ref_operators, SpanlessEq}; use if_chain::if_chain; use rustc_hir::LangItem::OptionNone; @@ -83,13 +83,12 @@ fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext // the "wild-like" branches must be equal if SpanlessEq::new(cx).eq_expr(wild_inner_arm.body, wild_outer_arm.body); // the binding must not be used in the if guard - let mut used_visitor = LocalUsedVisitor::new(cx, binding_id); if match arm.guard { None => true, - Some(Guard::If(expr) | Guard::IfLet(_, expr)) => !used_visitor.check_expr(expr), + Some(Guard::If(expr) | Guard::IfLet(_, expr)) => !is_local_used(cx, expr, binding_id), }; // ...or anywhere in the inner match - if !arms_inner.iter().any(|arm| used_visitor.check_arm(arm)); + if !arms_inner.iter().any(|arm| is_local_used(cx, arm, binding_id)); then { span_lint_and_then( cx, diff --git a/clippy_lints/src/let_if_seq.rs b/clippy_lints/src/let_if_seq.rs index 13f0d43cf8dd1..834440e912dfa 100644 --- a/clippy_lints/src/let_if_seq.rs +++ b/clippy_lints/src/let_if_seq.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; -use clippy_utils::{path_to_local_id, visitors::LocalUsedVisitor}; +use clippy_utils::{path_to_local_id, visitors::is_local_used}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -65,11 +65,10 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq { if let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind; if let hir::StmtKind::Expr(if_) = expr.kind; if let hir::ExprKind::If(cond, then, ref else_) = if_.kind; - let mut used_visitor = LocalUsedVisitor::new(cx, canonical_id); - if !used_visitor.check_expr(cond); + if !is_local_used(cx, cond, canonical_id); if let hir::ExprKind::Block(then, _) = then.kind; if let Some(value) = check_assign(cx, canonical_id, &*then); - if !used_visitor.check_expr(value); + if !is_local_used(cx, value, canonical_id); then { let span = stmt.span.to(if_.span); @@ -148,15 +147,13 @@ fn check_assign<'tcx>( if let hir::ExprKind::Assign(var, value, _) = expr.kind; if path_to_local_id(var, decl); then { - let mut v = LocalUsedVisitor::new(cx, decl); - - if block.stmts.iter().take(block.stmts.len()-1).any(|stmt| v.check_stmt(stmt)) { - return None; + if block.stmts.iter().take(block.stmts.len()-1).any(|stmt| is_local_used(cx, stmt, decl)) { + None + } else { + Some(value) } - - return Some(value); + } else { + None } } - - None } diff --git a/clippy_lints/src/loops/for_kv_map.rs b/clippy_lints/src/loops/for_kv_map.rs index 82bf49f5b49a4..68bef2f4c8bbb 100644 --- a/clippy_lints/src/loops/for_kv_map.rs +++ b/clippy_lints/src/loops/for_kv_map.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::sugg; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::visitors::LocalUsedVisitor; +use clippy_utils::visitors::is_local_used; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty; @@ -66,9 +66,7 @@ pub(super) fn check<'tcx>( fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: &'tcx Expr<'_>) -> bool { match *pat { PatKind::Wild => true, - PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => { - !LocalUsedVisitor::new(cx, id).check_expr(body) - }, + PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => !is_local_used(cx, body, id), _ => false, } } diff --git a/clippy_lints/src/loops/manual_flatten.rs b/clippy_lints/src/loops/manual_flatten.rs index d8153abc9653b..2af6cfe35b54a 100644 --- a/clippy_lints/src/loops/manual_flatten.rs +++ b/clippy_lints/src/loops/manual_flatten.rs @@ -1,7 +1,7 @@ use super::utils::make_iterator_snippet; use super::MANUAL_FLATTEN; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::visitors::LocalUsedVisitor; +use clippy_utils::visitors::is_local_used; use clippy_utils::{is_lang_ctor, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -49,7 +49,7 @@ pub(super) fn check<'tcx>( let ok_ctor = is_lang_ctor(cx, qpath, ResultOk); if some_ctor || ok_ctor; // Ensure epxr in `if let` is not used afterwards - if !LocalUsedVisitor::new(cx, pat_hir_id).check_arm(true_arm); + if !is_local_used(cx, true_arm, pat_hir_id); then { let if_let_type = if some_ctor { "Some" } else { "Ok" }; // Prepare the error message diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 3810d0dcc051a..fa33d068d3d88 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -2,10 +2,8 @@ use super::NEEDLESS_RANGE_LOOP; use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::ty::has_iter_method; -use clippy_utils::visitors::LocalUsedVisitor; -use clippy_utils::{ - contains_name, higher, is_integer_const, match_trait_method, path_to_local_id, paths, sugg, SpanlessEq, -}; +use clippy_utils::visitors::is_local_used; +use clippy_utils::{contains_name, higher, is_integer_const, match_trait_method, paths, sugg, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -256,43 +254,36 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { if let ExprKind::Path(ref seqpath) = seqexpr.kind; if let QPath::Resolved(None, seqvar) = *seqpath; if seqvar.segments.len() == 1; - let index_used_directly = path_to_local_id(idx, self.var); - let indexed_indirectly = { - let mut used_visitor = LocalUsedVisitor::new(self.cx, self.var); - walk_expr(&mut used_visitor, idx); - used_visitor.used - }; - if indexed_indirectly || index_used_directly; + if is_local_used(self.cx, idx, self.var); then { if self.prefer_mutable { self.indexed_mut.insert(seqvar.segments[0].ident.name); } + let index_used_directly = matches!(idx.kind, ExprKind::Path(_)); let res = self.cx.qpath_res(seqpath, seqexpr.hir_id); match res { Res::Local(hir_id) => { let parent_id = self.cx.tcx.hir().get_parent_item(expr.hir_id); let parent_def_id = self.cx.tcx.hir().local_def_id(parent_id); let extent = self.cx.tcx.region_scope_tree(parent_def_id).var_scope(hir_id.local_id); - if indexed_indirectly { - self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent)); - } if index_used_directly { self.indexed_directly.insert( seqvar.segments[0].ident.name, (Some(extent), self.cx.typeck_results().node_type(seqexpr.hir_id)), ); + } else { + self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent)); } return false; // no need to walk further *on the variable* } Res::Def(DefKind::Static | DefKind::Const, ..) => { - if indexed_indirectly { - self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None); - } if index_used_directly { self.indexed_directly.insert( seqvar.segments[0].ident.name, (None, self.cx.typeck_results().node_type(seqexpr.hir_id)), ); + } else { + self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None); } return false; // no need to walk further *on the variable* } diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 5360c02f90539..149c9bee9fbd5 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -5,7 +5,7 @@ use clippy_utils::diagnostics::{ use clippy_utils::source::{expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs}; -use clippy_utils::visitors::LocalUsedVisitor; +use clippy_utils::visitors::is_local_used; use clippy_utils::{ get_parent_expr, in_macro, is_expn_of, is_lang_ctor, is_lint_allowed, is_refutable, is_wild, meets_msrv, msrvs, path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks, @@ -953,9 +953,7 @@ fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm // Looking for unused bindings (i.e.: `_e`) for pat in inner.iter() { if let PatKind::Binding(_, id, ident, None) = pat.kind { - if ident.as_str().starts_with('_') - && !LocalUsedVisitor::new(cx, id).check_expr(arm.body) - { + if ident.as_str().starts_with('_') && !is_local_used(cx, arm.body, id) { ident_bind_name = (&ident.name.as_str()).to_string(); matching_wild = true; } diff --git a/clippy_lints/src/unused_self.rs b/clippy_lints/src/unused_self.rs index 658ac81f6eac8..e7e249c79a2fa 100644 --- a/clippy_lints/src/unused_self.rs +++ b/clippy_lints/src/unused_self.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::visitors::LocalUsedVisitor; +use clippy_utils::visitors::is_local_used; use if_chain::if_chain; use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -50,8 +50,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf { if let ImplItemKind::Fn(.., body_id) = &impl_item.kind; let body = cx.tcx.hir().body(*body_id); if let [self_param, ..] = body.params; - let self_hir_id = self_param.pat.hir_id; - if !LocalUsedVisitor::new(cx, self_hir_id).check_body(body); + if !is_local_used(cx, body, self_param.pat.hir_id); then { span_lint_and_help( cx, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index bd229402f4182..603e831459d38 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2,6 +2,7 @@ #![feature(in_band_lifetimes)] #![feature(iter_zip)] #![feature(rustc_private)] +#![feature(control_flow_enum)] #![recursion_limit = "512"] #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)] diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index ce00106dd4d80..52f95ff34f505 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -4,6 +4,7 @@ use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visito use rustc_hir::{def::Res, Arm, Block, Body, BodyId, Destination, Expr, ExprKind, HirId, Stmt}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; +use std::ops::ControlFlow; /// returns `true` if expr contains match expr desugared from try fn contains_try(expr: &hir::Expr<'_>) -> bool { @@ -133,62 +134,6 @@ where } } -pub struct LocalUsedVisitor<'hir> { - hir: Map<'hir>, - pub local_hir_id: HirId, - pub used: bool, -} - -impl<'hir> LocalUsedVisitor<'hir> { - pub fn new(cx: &LateContext<'hir>, local_hir_id: HirId) -> Self { - Self { - hir: cx.tcx.hir(), - local_hir_id, - used: false, - } - } - - fn check(&mut self, t: T, visit: fn(&mut Self, T)) -> bool { - visit(self, t); - std::mem::replace(&mut self.used, false) - } - - pub fn check_arm(&mut self, arm: &'hir Arm<'_>) -> bool { - self.check(arm, Self::visit_arm) - } - - pub fn check_body(&mut self, body: &'hir Body<'_>) -> bool { - self.check(body, Self::visit_body) - } - - pub fn check_expr(&mut self, expr: &'hir Expr<'_>) -> bool { - self.check(expr, Self::visit_expr) - } - - pub fn check_stmt(&mut self, stmt: &'hir Stmt<'_>) -> bool { - self.check(stmt, Self::visit_stmt) - } -} - -impl<'v> Visitor<'v> for LocalUsedVisitor<'v> { - type Map = Map<'v>; - - fn visit_expr(&mut self, expr: &'v Expr<'v>) { - if self.used { - return; - } - if path_to_local_id(expr, self.local_hir_id) { - self.used = true; - } else { - walk_expr(self, expr); - } - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::OnlyBodies(self.hir) - } -} - /// A type which can be visited. pub trait Visitable<'tcx> { /// Calls the corresponding `visit_*` function on the visitor. @@ -202,8 +147,22 @@ macro_rules! visitable_ref { } } }; + ([$t:ident], $f:ident) => { + impl Visitable<'tcx> for &'tcx [$t<'tcx>] { + fn visit>(self, visitor: &mut V) { + for x in self { + visitor.$f(x); + } + } + } + }; } +visitable_ref!(Arm, visit_arm); visitable_ref!(Block, visit_block); +visitable_ref!(Body, visit_body); +visitable_ref!(Expr, visit_expr); +visitable_ref!(Stmt, visit_stmt); +visitable_ref!([Stmt], visit_stmt); /// Calls the given function for each break expression. pub fn visit_break_exprs<'tcx>( @@ -260,3 +219,48 @@ pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool { v.visit_expr(&cx.tcx.hir().body(body).value); v.found } + +/// Calls the given function for each usage of the given local. +pub fn for_each_local_usage<'tcx, B>( + cx: &LateContext<'tcx>, + visitable: impl Visitable<'tcx>, + id: HirId, + f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow, +) -> ControlFlow { + struct V<'tcx, B, F> { + map: Map<'tcx>, + id: HirId, + f: F, + res: ControlFlow, + } + impl<'tcx, B, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow> Visitor<'tcx> for V<'tcx, B, F> { + type Map = Map<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.map) + } + + fn visit_expr(&mut self, e: &'tcx Expr<'_>) { + if self.res.is_continue() { + if path_to_local_id(e, self.id) { + self.res = (self.f)(e); + } else { + walk_expr(self, e); + } + } + } + } + + let mut v = V { + map: cx.tcx.hir(), + id, + f, + res: ControlFlow::CONTINUE, + }; + visitable.visit(&mut v); + v.res +} + +/// Checks if the given local is used. +pub fn is_local_used(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool { + for_each_local_usage(cx, visitable, id, |_| ControlFlow::BREAK).is_break() +} From a7f376fbd3f30b58ec2bde5538b446eb4c2d615e Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 17 Jul 2021 13:52:03 -0400 Subject: [PATCH 23/94] Add lint `manual_split_once` --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 + clippy_lints/src/methods/manual_split_once.rs | 213 ++++++++++++++++++ clippy_lints/src/methods/mod.rs | 44 +++- clippy_lints/src/methods/suspicious_splitn.rs | 14 +- clippy_lints/src/utils/conf.rs | 2 +- clippy_utils/src/diagnostics.rs | 2 +- clippy_utils/src/msrvs.rs | 1 + clippy_utils/src/paths.rs | 1 + clippy_utils/src/visitors.rs | 21 +- tests/compile-test.rs | 1 + tests/ui/manual_split_once.fixed | 50 ++++ tests/ui/manual_split_once.rs | 50 ++++ tests/ui/manual_split_once.stderr | 82 +++++++ 14 files changed, 459 insertions(+), 26 deletions(-) create mode 100644 clippy_lints/src/methods/manual_split_once.rs create mode 100644 tests/ui/manual_split_once.fixed create mode 100644 tests/ui/manual_split_once.rs create mode 100644 tests/ui/manual_split_once.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b89170073be5..b9e67d361c0c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2754,6 +2754,7 @@ Released 2018-09-13 [`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or [`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic +[`manual_split_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once [`manual_str_repeat`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat [`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip [`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 8372d681078df..1fadaf4770a81 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -773,6 +773,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: methods::MANUAL_FILTER_MAP, methods::MANUAL_FIND_MAP, methods::MANUAL_SATURATING_ARITHMETIC, + methods::MANUAL_SPLIT_ONCE, methods::MANUAL_STR_REPEAT, methods::MAP_COLLECT_RESULT_UNIT, methods::MAP_FLATTEN, @@ -1319,6 +1320,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(methods::MANUAL_FILTER_MAP), LintId::of(methods::MANUAL_FIND_MAP), LintId::of(methods::MANUAL_SATURATING_ARITHMETIC), + LintId::of(methods::MANUAL_SPLIT_ONCE), LintId::of(methods::MANUAL_STR_REPEAT), LintId::of(methods::MAP_COLLECT_RESULT_UNIT), LintId::of(methods::MAP_IDENTITY), @@ -1617,6 +1619,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(methods::ITER_COUNT), LintId::of(methods::MANUAL_FILTER_MAP), LintId::of(methods::MANUAL_FIND_MAP), + LintId::of(methods::MANUAL_SPLIT_ONCE), LintId::of(methods::MAP_IDENTITY), LintId::of(methods::OPTION_AS_REF_DEREF), LintId::of(methods::OPTION_FILTER_MAP), diff --git a/clippy_lints/src/methods/manual_split_once.rs b/clippy_lints/src/methods/manual_split_once.rs new file mode 100644 index 0000000000000..e273186d05190 --- /dev/null +++ b/clippy_lints/src/methods/manual_split_once.rs @@ -0,0 +1,213 @@ +use clippy_utils::consts::{constant, Constant}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_context; +use clippy_utils::{is_diag_item_method, match_def_path, paths}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, HirId, LangItem, Node, QPath}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, adjustment::Adjust}; +use rustc_span::{symbol::sym, Span, SyntaxContext}; + +use super::MANUAL_SPLIT_ONCE; + +pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>) { + if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() { + return; + } + + let ctxt = expr.span.ctxt(); + let usage = match parse_iter_usage(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id)) { + Some(x) => x, + None => return, + }; + let (method_name, msg) = if method_name == "splitn" { + ("split_once", "manual implementation of `split_once`") + } else { + ("rsplit_once", "manual implementation of `rsplit_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; + + match usage.kind { + IterUsageKind::NextTuple => { + span_lint_and_sugg( + cx, + MANUAL_SPLIT_ONCE, + usage.span, + msg, + "try this", + format!("{}.{}({})", self_snip, method_name, pat_snip), + app, + ); + }, + IterUsageKind::Next => { + let self_deref = { + let adjust = cx.typeck_results().expr_adjustments(self_arg); + if adjust.is_empty() { + String::new() + } else if cx.typeck_results().expr_ty(self_arg).is_box() + || adjust + .iter() + .any(|a| matches!(a.kind, Adjust::Deref(Some(_))) || a.target.is_box()) + { + format!("&{}", "*".repeat(adjust.len() - 1)) + } else { + "*".repeat(adjust.len() - 2) + } + }; + let sugg = if usage.unwrap_kind.is_some() { + format!( + "{}.{}({}).map_or({}{}, |x| x.0)", + &self_snip, method_name, pat_snip, self_deref, &self_snip + ) + } else { + format!( + "Some({}.{}({}).map_or({}{}, |x| x.0))", + &self_snip, method_name, pat_snip, self_deref, &self_snip + ) + }; + + span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app); + }, + IterUsageKind::Second => { + let access_str = match usage.unwrap_kind { + Some(UnwrapKind::Unwrap) => ".unwrap().1", + Some(UnwrapKind::QuestionMark) => "?.1", + None => ".map(|x| x.1)", + }; + span_lint_and_sugg( + cx, + MANUAL_SPLIT_ONCE, + usage.span, + msg, + "try this", + format!("{}.{}({}){}", self_snip, method_name, pat_snip, access_str), + app, + ); + }, + } +} + +enum IterUsageKind { + Next, + Second, + NextTuple, +} + +enum UnwrapKind { + Unwrap, + QuestionMark, +} + +struct IterUsage { + kind: IterUsageKind, + unwrap_kind: Option, + span: Span, +} + +fn parse_iter_usage( + cx: &LateContext<'tcx>, + ctxt: SyntaxContext, + mut iter: impl Iterator)>, +) -> Option { + let (kind, span) = match iter.next() { + Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => { + let (name, args) = if let ExprKind::MethodCall(name, _, [_, args @ ..], _) = e.kind { + (name, args) + } else { + return None; + }; + let did = cx.typeck_results().type_dependent_def_id(e.hir_id)?; + let iter_id = cx.tcx.get_diagnostic_item(sym::Iterator)?; + + match (&*name.ident.as_str(), args) { + ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Next, e.span), + ("next_tuple", []) => { + if_chain! { + if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE); + if let ty::Adt(adt_def, subs) = cx.typeck_results().expr_ty(e).kind(); + if cx.tcx.is_diagnostic_item(sym::option_type, adt_def.did); + if let ty::Tuple(subs) = subs.type_at(0).kind(); + if subs.len() == 2; + then { + return Some(IterUsage { kind: IterUsageKind::NextTuple, span: e.span, unwrap_kind: None }); + } else { + return None; + } + } + }, + ("nth" | "skip", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => { + if let Some((Constant::Int(idx), _)) = constant(cx, cx.typeck_results(), idx_expr) { + let span = if name.ident.as_str() == "nth" { + e.span + } else { + if_chain! { + if let Some((_, Node::Expr(next_expr))) = iter.next(); + if let ExprKind::MethodCall(next_name, _, [_], _) = next_expr.kind; + if next_name.ident.name == sym::next; + if next_expr.span.ctxt() == ctxt; + if let Some(next_id) = cx.typeck_results().type_dependent_def_id(next_expr.hir_id); + if cx.tcx.trait_of_item(next_id) == Some(iter_id); + then { + next_expr.span + } else { + return None; + } + } + }; + match idx { + 0 => (IterUsageKind::Next, span), + 1 => (IterUsageKind::Second, span), + _ => return None, + } + } else { + return None; + } + }, + _ => return None, + } + }, + _ => return None, + }; + + let (unwrap_kind, span) = if let Some((_, Node::Expr(e))) = iter.next() { + match e.kind { + ExprKind::Call( + Expr { + kind: ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, _)), + .. + }, + _, + ) => { + let parent_span = e.span.parent().unwrap(); + if parent_span.ctxt() == ctxt { + (Some(UnwrapKind::QuestionMark), parent_span) + } else { + (None, span) + } + }, + _ if e.span.ctxt() != ctxt => (None, span), + ExprKind::MethodCall(name, _, [_], _) + if name.ident.name == sym::unwrap + && cx + .typeck_results() + .type_dependent_def_id(e.hir_id) + .map_or(false, |id| is_diag_item_method(cx, id, sym::option_type)) => + { + (Some(UnwrapKind::Unwrap), e.span) + }, + _ => (None, span), + } + } else { + (None, span) + }; + + Some(IterUsage { + kind, + unwrap_kind, + span, + }) +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 91606ed3b2bb0..15ad0325006eb 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -33,6 +33,7 @@ mod iter_nth_zero; mod iter_skip_next; mod iterator_step_by_zero; mod manual_saturating_arithmetic; +mod manual_split_once; mod manual_str_repeat; mod map_collect_result_unit; mod map_flatten; @@ -64,6 +65,7 @@ mod wrong_self_convention; mod zst_offset; use bind_instead_of_map::BindInsteadOfMap; +use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::ty::{contains_adt_constructor, contains_ty, implements_trait, is_copy, is_type_diagnostic_item}; use clippy_utils::{contains_return, get_trait_def_id, in_macro, iter_input_pats, meets_msrv, msrvs, paths, return_ty}; @@ -1771,6 +1773,31 @@ declare_clippy_lint! { "manual implementation of `str::repeat`" } +declare_clippy_lint! { + /// **What it does:** Checks for usages of `splitn(2, _)` + /// + /// **Why is this bad?** `split_once` is clearer. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // Bad + /// let some_str = "name=value"; + /// let mut iter = some_str.splitn(2, '='); + /// let name = iter.next().unwrap(); + /// let value = iter.next().unwrap_or(""); + /// + /// // Good + /// let some_str = "name=value"; + /// let (name, value) = some_str.split_once('=').unwrap_or((some_str, "")); + /// ``` + pub MANUAL_SPLIT_ONCE, + complexity, + "replace `.splitn(2, pat)` with `.split_once(pat)`" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Option, @@ -1848,7 +1875,8 @@ impl_lint_pass!(Methods => [ IMPLICIT_CLONE, SUSPICIOUS_SPLITN, MANUAL_STR_REPEAT, - EXTEND_WITH_DRAIN + EXTEND_WITH_DRAIN, + MANUAL_SPLIT_ONCE ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -2176,8 +2204,18 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio unnecessary_lazy_eval::check(cx, expr, recv, arg, "or"); } }, - ("splitn" | "splitn_mut" | "rsplitn" | "rsplitn_mut", [count_arg, _]) => { - suspicious_splitn::check(cx, name, expr, recv, count_arg); + ("splitn" | "rsplitn", [count_arg, pat_arg]) => { + if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) { + suspicious_splitn::check(cx, name, expr, recv, count); + if count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE) { + manual_split_once::check(cx, name, expr, recv, pat_arg); + } + } + }, + ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => { + if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) { + suspicious_splitn::check(cx, name, expr, recv, count); + } }, ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg), ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => { diff --git a/clippy_lints/src/methods/suspicious_splitn.rs b/clippy_lints/src/methods/suspicious_splitn.rs index a271df60572a2..1c546a15bf62b 100644 --- a/clippy_lints/src/methods/suspicious_splitn.rs +++ b/clippy_lints/src/methods/suspicious_splitn.rs @@ -1,4 +1,3 @@ -use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_note; use if_chain::if_chain; use rustc_ast::LitKind; @@ -8,15 +7,8 @@ use rustc_span::source_map::Spanned; use super::SUSPICIOUS_SPLITN; -pub(super) fn check( - cx: &LateContext<'_>, - method_name: &str, - expr: &Expr<'_>, - self_arg: &Expr<'_>, - count_arg: &Expr<'_>, -) { +pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, count: u128) { if_chain! { - if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg); if count <= 1; if let Some(call_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let Some(impl_id) = cx.tcx.impl_of_method(call_id); @@ -24,9 +16,9 @@ pub(super) fn check( if lang_items.slice_impl() == Some(impl_id) || lang_items.str_impl() == Some(impl_id); then { // Ignore empty slice and string literals when used with a literal count. - if (matches!(self_arg.kind, ExprKind::Array([])) + if matches!(self_arg.kind, ExprKind::Array([])) || matches!(self_arg.kind, ExprKind::Lit(Spanned { node: LitKind::Str(s, _), .. }) if s.is_empty()) - ) && matches!(count_arg.kind, ExprKind::Lit(_)) + { return; } diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 9ee2e3024520b..211ffdeca2000 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -136,7 +136,7 @@ define_Conf! { /// /// Suppress lints whenever the suggested change would cause breakage for other crates. (avoid_breaking_exported_api: bool = true), - /// Lint: MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE. /// /// The minimum rust version that the project supports (msrv: Option = None), diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index 71cfa196fc335..9302e5c21faa4 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -21,7 +21,7 @@ fn docs_link(diag: &mut DiagnosticBuilder<'_>, lint: &'static Lint) { "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{}", &option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| { // extract just major + minor version and ignore patch versions - format!("rust-{}", n.rsplitn(2, '.').nth(1).unwrap()) + format!("rust-{}", n.rsplit_once('.').unwrap().1) }), lint )); diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 4a9c4fd0276b3..14234d9c9cbf0 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -13,6 +13,7 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { 1,53,0 { OR_PATTERNS } + 1,52,0 { STR_SPLIT_ONCE } 1,50,0 { BOOL_THEN } 1,46,0 { CONST_IF_MATCH } 1,45,0 { STR_STRIP_PREFIX } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index b0c3fe1e5a712..d7e46c2d3eb9d 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -68,6 +68,7 @@ pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"]; pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"]; pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"]; pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"]; +pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"]; #[cfg(feature = "internal-lints")] pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; #[cfg(feature = "internal-lints")] diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index 52f95ff34f505..503effbdad572 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -147,22 +147,23 @@ macro_rules! visitable_ref { } } }; - ([$t:ident], $f:ident) => { - impl Visitable<'tcx> for &'tcx [$t<'tcx>] { - fn visit>(self, visitor: &mut V) { - for x in self { - visitor.$f(x); - } - } - } - }; } visitable_ref!(Arm, visit_arm); visitable_ref!(Block, visit_block); visitable_ref!(Body, visit_body); visitable_ref!(Expr, visit_expr); visitable_ref!(Stmt, visit_stmt); -visitable_ref!([Stmt], visit_stmt); + +// impl<'tcx, I: IntoIterator> Visitable<'tcx> for I +// where +// I::Item: Visitable<'tcx>, +// { +// fn visit>(self, visitor: &mut V) { +// for x in self { +// x.visit(visitor); +// } +// } +// } /// Calls the given function for each break expression. pub fn visit_break_exprs<'tcx>( diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 0a82377a10e42..6116acffe07f4 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -39,6 +39,7 @@ fn third_party_crates() -> String { "clippy_lints", "clippy_utils", "if_chain", + "itertools", "quote", "regex", "serde", diff --git a/tests/ui/manual_split_once.fixed b/tests/ui/manual_split_once.fixed new file mode 100644 index 0000000000000..3a0332939d409 --- /dev/null +++ b/tests/ui/manual_split_once.fixed @@ -0,0 +1,50 @@ +// run-rustfix + +#![feature(custom_inner_attributes)] +#![warn(clippy::manual_split_once)] +#![allow(clippy::iter_skip_next, clippy::iter_nth_zero)] + +extern crate itertools; + +#[allow(unused_imports)] +use itertools::Itertools; + +fn main() { + let _ = Some("key=value".split_once('=').map_or("key=value", |x| x.0)); + let _ = "key=value".splitn(2, '=').nth(2); + let _ = "key=value".split_once('=').map_or("key=value", |x| x.0); + let _ = "key=value".split_once('=').map_or("key=value", |x| x.0); + let _ = "key=value".split_once('=').unwrap().1; + let _ = "key=value".split_once('=').unwrap().1; + let (_, _) = "key=value".split_once('=').unwrap(); + + let s = String::from("key=value"); + let _ = s.split_once('=').map_or(&*s, |x| x.0); + + let s = Box::::from("key=value"); + let _ = s.split_once('=').map_or(&*s, |x| x.0); + + let s = &"key=value"; + let _ = s.split_once('=').map_or(*s, |x| x.0); + + fn _f(s: &str) -> Option<&str> { + let _ = s.split_once("key=value").map_or(s, |x| x.0); + let _ = s.split_once("key=value")?.1; + let _ = s.split_once("key=value")?.1; + None + } + + // Don't lint, slices don't have `split_once` + let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap(); +} + +fn _msrv_1_51() { + #![clippy::msrv = "1.51"] + // `str::split_once` was stabilized in 1.16. Do not lint this + let _ = "key=value".splitn(2, '=').nth(1).unwrap(); +} + +fn _msrv_1_52() { + #![clippy::msrv = "1.52"] + let _ = "key=value".split_once('=').unwrap().1; +} diff --git a/tests/ui/manual_split_once.rs b/tests/ui/manual_split_once.rs new file mode 100644 index 0000000000000..e6093b63fe8d4 --- /dev/null +++ b/tests/ui/manual_split_once.rs @@ -0,0 +1,50 @@ +// run-rustfix + +#![feature(custom_inner_attributes)] +#![warn(clippy::manual_split_once)] +#![allow(clippy::iter_skip_next, clippy::iter_nth_zero)] + +extern crate itertools; + +#[allow(unused_imports)] +use itertools::Itertools; + +fn main() { + let _ = "key=value".splitn(2, '=').next(); + let _ = "key=value".splitn(2, '=').nth(2); + let _ = "key=value".splitn(2, '=').next().unwrap(); + let _ = "key=value".splitn(2, '=').nth(0).unwrap(); + let _ = "key=value".splitn(2, '=').nth(1).unwrap(); + let _ = "key=value".splitn(2, '=').skip(1).next().unwrap(); + let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap(); + + let s = String::from("key=value"); + let _ = s.splitn(2, '=').next().unwrap(); + + let s = Box::::from("key=value"); + let _ = s.splitn(2, '=').nth(0).unwrap(); + + let s = &"key=value"; + let _ = s.splitn(2, '=').skip(0).next().unwrap(); + + fn _f(s: &str) -> Option<&str> { + let _ = s.splitn(2, "key=value").next()?; + let _ = s.splitn(2, "key=value").nth(1)?; + let _ = s.splitn(2, "key=value").skip(1).next()?; + None + } + + // Don't lint, slices don't have `split_once` + let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap(); +} + +fn _msrv_1_51() { + #![clippy::msrv = "1.51"] + // `str::split_once` was stabilized in 1.16. Do not lint this + let _ = "key=value".splitn(2, '=').nth(1).unwrap(); +} + +fn _msrv_1_52() { + #![clippy::msrv = "1.52"] + let _ = "key=value".splitn(2, '=').nth(1).unwrap(); +} diff --git a/tests/ui/manual_split_once.stderr b/tests/ui/manual_split_once.stderr new file mode 100644 index 0000000000000..4f15196b469e1 --- /dev/null +++ b/tests/ui/manual_split_once.stderr @@ -0,0 +1,82 @@ +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:13:13 + | +LL | let _ = "key=value".splitn(2, '=').next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Some("key=value".split_once('=').map_or("key=value", |x| x.0))` + | + = note: `-D clippy::manual-split-once` implied by `-D warnings` + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:15:13 + | +LL | let _ = "key=value".splitn(2, '=').next().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').map_or("key=value", |x| x.0)` + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:16:13 + | +LL | let _ = "key=value".splitn(2, '=').nth(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').map_or("key=value", |x| x.0)` + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:17:13 + | +LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:18:13 + | +LL | let _ = "key=value".splitn(2, '=').skip(1).next().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:19:18 + | +LL | let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=')` + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:22:13 + | +LL | let _ = s.splitn(2, '=').next().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(&*s, |x| x.0)` + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:25:13 + | +LL | let _ = s.splitn(2, '=').nth(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(&*s, |x| x.0)` + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:28:13 + | +LL | let _ = s.splitn(2, '=').skip(0).next().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(*s, |x| x.0)` + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:31:17 + | +LL | let _ = s.splitn(2, "key=value").next()?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value").map_or(s, |x| x.0)` + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:32:17 + | +LL | let _ = s.splitn(2, "key=value").nth(1)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1` + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:33:17 + | +LL | let _ = s.splitn(2, "key=value").skip(1).next()?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1` + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:49:13 + | +LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` + +error: aborting due to 13 previous errors + From aab3267412bd23cae4fd58e860b6b9a36de42448 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 15 Aug 2021 16:27:59 -0400 Subject: [PATCH 24/94] Update docs for `manual_split_once` --- clippy_lints/src/methods/mod.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 15ad0325006eb..b6438bce8746e 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1774,24 +1774,22 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for usages of `splitn(2, _)` + /// **What it does:** Checks for usages of `str::splitn(2, _)` /// - /// **Why is this bad?** `split_once` is clearer. + /// **Why is this bad?** `split_once` is both clearer in intent and slightly more efficient. /// /// **Known problems:** None. /// /// **Example:** /// - /// ```rust + /// ```rust,ignore /// // Bad - /// let some_str = "name=value"; - /// let mut iter = some_str.splitn(2, '='); - /// let name = iter.next().unwrap(); - /// let value = iter.next().unwrap_or(""); + /// let (key, value) = _.splitn(2, '=').next_tuple()?; + /// let value = _.splitn(2, '=').nth(1)?; /// /// // Good - /// let some_str = "name=value"; - /// let (name, value) = some_str.split_once('=').unwrap_or((some_str, "")); + /// let (key, value) = _.split_once('=')?; + /// let value = _.split_once('=')?.1; /// ``` pub MANUAL_SPLIT_ONCE, complexity, From d5e51dbf10c116f899abc275a6e36b8977b0ebfa Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 16 Jul 2021 08:54:08 -0500 Subject: [PATCH 25/94] clippy: Fix format_args expansion parsing --- clippy_utils/src/higher.rs | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 29b698e56e3c0..6437363bad74c 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -485,12 +485,28 @@ impl FormatArgsExpn<'tcx> { if let ExpnKind::Macro(_, name) = expr.span.ctxt().outer_expn_data().kind; let name = name.as_str(); if name.ends_with("format_args") || name.ends_with("format_args_nl"); - if let ExprKind::Call(_, args) = expr.kind; - if let Some((strs_ref, args, fmt_expr)) = match args { + + if let ExprKind::Match(inner_match, [arm], _) = expr.kind; + + // `match match`, if you will + if let ExprKind::Match(args, [inner_arm], _) = inner_match.kind; + if let ExprKind::Tup(value_args) = args.kind; + if let Some(value_args) = value_args + .iter() + .map(|e| match e.kind { + ExprKind::AddrOf(_, _, e) => Some(e), + _ => None, + }) + .collect(); + if let ExprKind::Array(args) = inner_arm.body.kind; + + if let ExprKind::Block(Block { stmts: [], expr: Some(expr), .. }, _) = arm.body.kind; + if let ExprKind::Call(_, call_args) = expr.kind; + if let Some((strs_ref, fmt_expr)) = match call_args { // Arguments::new_v1 - [strs_ref, args] => Some((strs_ref, args, None)), + [strs_ref, _] => Some((strs_ref, None)), // Arguments::new_v1_formatted - [strs_ref, args, fmt_expr] => Some((strs_ref, args, Some(fmt_expr))), + [strs_ref, _, fmt_expr] => Some((strs_ref, Some(fmt_expr))), _ => None, }; if let ExprKind::AddrOf(BorrowKind::Ref, _, strs_arr) = strs_ref.kind; @@ -506,17 +522,6 @@ impl FormatArgsExpn<'tcx> { None }) .collect(); - if let ExprKind::AddrOf(BorrowKind::Ref, _, args) = args.kind; - if let ExprKind::Match(args, [arm], _) = args.kind; - if let ExprKind::Tup(value_args) = args.kind; - if let Some(value_args) = value_args - .iter() - .map(|e| match e.kind { - ExprKind::AddrOf(_, _, e) => Some(e), - _ => None, - }) - .collect(); - if let ExprKind::Array(args) = arm.body.kind; then { Some(FormatArgsExpn { format_string_span: strs_ref.span, From 8b3ca9a315aeb9ece9ca9df27e69f581629c51aa Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 16 Aug 2021 15:42:52 -0400 Subject: [PATCH 26/94] Fix `option_if_let_else` * `break` and `continue` statments local to the would-be closure are allowed * don't lint in const contexts * don't lint when yield expressions are used * don't lint when the captures made by the would-be closure conflict with the other branch * don't lint when a field of a local is used when the type could be pontentially moved from * in some cases, don't lint when scrutinee expression conflicts with the captures of the would-be closure --- clippy_lints/src/option_if_let_else.rs | 53 ++++++++++++++++++++------ clippy_utils/src/lib.rs | 5 +++ tests/ui/option_if_let_else.fixed | 42 ++++++++++++++++++++ tests/ui/option_if_let_else.rs | 48 +++++++++++++++++++++++ tests/ui/option_if_let_else.stderr | 50 +++++++++++++++++++++++- 5 files changed, 186 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index a7899dc8bc126..46f06362ccc67 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -1,12 +1,16 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::usage::contains_return_break_continue_macro; -use clippy_utils::{eager_or_lazy, in_macro, is_else_clause, is_lang_ctor}; +use clippy_utils::{ + can_move_expr_to_closure, eager_or_lazy, in_constant, in_macro, is_else_clause, is_lang_ctor, peel_hir_expr_while, + CaptureKind, +}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionSome; -use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp}; +use rustc_hir::{ + def::Res, Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, Path, QPath, UnOp, +}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -127,21 +131,30 @@ fn detect_option_if_let_else<'tcx>( ) -> Option { if_chain! { if !in_macro(expr.span); // Don't lint macros, because it behaves weirdly - if let ExprKind::Match(cond_expr, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind; + if !in_constant(cx, expr.hir_id); + if let ExprKind::Match(cond_expr, [some_arm, none_arm], MatchSource::IfLetDesugar{contains_else_clause: true}) + = &expr.kind; if !is_else_clause(cx.tcx, expr); - if arms.len() == 2; if !is_result_ok(cx, cond_expr); // Don't lint on Result::ok because a different lint does it already - if let PatKind::TupleStruct(struct_qpath, [inner_pat], _) = &arms[0].pat.kind; + if let PatKind::TupleStruct(struct_qpath, [inner_pat], _) = &some_arm.pat.kind; if is_lang_ctor(cx, struct_qpath, OptionSome); if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind; - if !contains_return_break_continue_macro(arms[0].body); - if !contains_return_break_continue_macro(arms[1].body); + if let Some(some_captures) = can_move_expr_to_closure(cx, some_arm.body); + if let Some(none_captures) = can_move_expr_to_closure(cx, none_arm.body); + if some_captures + .iter() + .filter_map(|(id, &c)| none_captures.get(id).map(|&c2| (c, c2))) + .all(|(x, y)| x.is_imm_ref() && y.is_imm_ref()); then { let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" }; - let some_body = extract_body_from_arm(&arms[0])?; - let none_body = extract_body_from_arm(&arms[1])?; - let method_sugg = if eager_or_lazy::is_eagerness_candidate(cx, none_body) { "map_or" } else { "map_or_else" }; + let some_body = extract_body_from_arm(some_arm)?; + let none_body = extract_body_from_arm(none_arm)?; + let method_sugg = if eager_or_lazy::is_eagerness_candidate(cx, none_body) { + "map_or" + } else { + "map_or_else" + }; let capture_name = id.name.to_ident_string(); let (as_ref, as_mut) = match &cond_expr.kind { ExprKind::AddrOf(_, Mutability::Not, _) => (true, false), @@ -153,6 +166,24 @@ fn detect_option_if_let_else<'tcx>( ExprKind::Unary(UnOp::Deref, expr) | ExprKind::AddrOf(_, _, expr) => expr, _ => cond_expr, }; + // Check if captures the closure will need conflict with borrows made in the scrutinee. + // TODO: check all the references made in the scrutinee expression. This will require interacting + // with the borrow checker. Currently only `[.]*` is checked for. + if as_ref || as_mut { + let e = peel_hir_expr_while(cond_expr, |e| match e.kind { + ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e), + _ => None, + }); + if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind { + match some_captures.get(l) + .or_else(|| (method_sugg == "map_or_else").then(|| ()).and_then(|_| none_captures.get(l))) + { + Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None, + Some(CaptureKind::Ref(Mutability::Not)) if as_mut => return None, + Some(CaptureKind::Ref(Mutability::Not)) | None => (), + } + } + } Some(OptionIfLetElseOccurence { option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut), method_sugg: method_sugg.to_string(), diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index bd229402f4182..bb7de1653602e 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -709,6 +709,11 @@ pub enum CaptureKind { Value, Ref(Mutability), } +impl CaptureKind { + pub fn is_imm_ref(self) -> bool { + self == Self::Ref(Mutability::Not) + } +} impl std::ops::BitOr for CaptureKind { type Output = Self; fn bitor(self, rhs: Self) -> Self::Output { diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index 769ccc14bc1e4..56e032cdb57aa 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -86,4 +86,46 @@ fn main() { test_map_or_else(None); let _ = negative_tests(None); let _ = impure_else(None); + + let _ = Some(0).map_or(0, |x| loop { + if x == 0 { + break x; + } + }); + + // #7576 + const fn _f(x: Option) -> u32 { + // Don't lint, `map_or` isn't const + if let Some(x) = x { x } else { 10 } + } + + // #5822 + let s = String::new(); + // Don't lint, `Some` branch consumes `s`, but else branch uses `s` + let _ = if let Some(x) = Some(0) { + let s = s; + s.len() + x + } else { + s.len() + }; + + let s = String::new(); + // Lint, both branches immutably borrow `s`. + let _ = Some(0).map_or_else(|| s.len(), |x| s.len() + x); + + let s = String::new(); + // Lint, `Some` branch consumes `s`, but else branch doesn't use `s`. + let _ = Some(0).map_or(1, |x| { + let s = s; + s.len() + x + }); + + let s = Some(String::new()); + // Don't lint, `Some` branch borrows `s`, but else branch consumes `s` + let _ = if let Some(x) = &s { + x.len() + } else { + let _s = s; + 10 + }; } diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index e2f8dec3b930c..595ed8172cd0e 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -105,4 +105,52 @@ fn main() { test_map_or_else(None); let _ = negative_tests(None); let _ = impure_else(None); + + let _ = if let Some(x) = Some(0) { + loop { + if x == 0 { + break x; + } + } + } else { + 0 + }; + + // #7576 + const fn _f(x: Option) -> u32 { + // Don't lint, `map_or` isn't const + if let Some(x) = x { x } else { 10 } + } + + // #5822 + let s = String::new(); + // Don't lint, `Some` branch consumes `s`, but else branch uses `s` + let _ = if let Some(x) = Some(0) { + let s = s; + s.len() + x + } else { + s.len() + }; + + let s = String::new(); + // Lint, both branches immutably borrow `s`. + let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() }; + + let s = String::new(); + // Lint, `Some` branch consumes `s`, but else branch doesn't use `s`. + let _ = if let Some(x) = Some(0) { + let s = s; + s.len() + x + } else { + 1 + }; + + let s = Some(String::new()); + // Don't lint, `Some` branch borrows `s`, but else branch consumes `s` + let _ = if let Some(x) = &s { + x.len() + } else { + let _s = s; + 10 + }; } diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index 099e49ef8e3dc..803d941c36df8 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -148,5 +148,53 @@ error: use Option::map_or instead of an if let/else LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` -error: aborting due to 11 previous errors +error: use Option::map_or instead of an if let/else + --> $DIR/option_if_let_else.rs:109:13 + | +LL | let _ = if let Some(x) = Some(0) { + | _____________^ +LL | | loop { +LL | | if x == 0 { +LL | | break x; +... | +LL | | 0 +LL | | }; + | |_____^ + | +help: try + | +LL ~ let _ = Some(0).map_or(0, |x| loop { +LL + if x == 0 { +LL + break x; +LL + } +LL ~ }); + | + +error: use Option::map_or_else instead of an if let/else + --> $DIR/option_if_let_else.rs:137:13 + | +LL | let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or_else(|| s.len(), |x| s.len() + x)` + +error: use Option::map_or instead of an if let/else + --> $DIR/option_if_let_else.rs:141:13 + | +LL | let _ = if let Some(x) = Some(0) { + | _____________^ +LL | | let s = s; +LL | | s.len() + x +LL | | } else { +LL | | 1 +LL | | }; + | |_____^ + | +help: try + | +LL ~ let _ = Some(0).map_or(1, |x| { +LL + let s = s; +LL + s.len() + x +LL ~ }); + | + +error: aborting due to 14 previous errors From 3c6c7d5de9cf989b19e82298735486a03eac4b75 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 16 Aug 2021 19:57:14 -0400 Subject: [PATCH 27/94] Update docs for `expl_impl_clone_on_copy`. Bug was fixed in 879fa5c9721c89c27be6a9db5f51d935a51f549b1~ --- clippy_lints/src/derive.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index dcfa5253f8341..8416b8440dfbe 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -105,9 +105,6 @@ declare_clippy_lint! { /// nothing more than copy the object, which is what `#[derive(Copy, Clone)]` /// gets you. /// - /// ### Known problems - /// Bounds of generic types are sometimes wrong: https://github.com/rust-lang/rust/issues/26925 - /// /// ### Example /// ```rust,ignore /// #[derive(Copy)] From c01aa64591df67b4f4b44c1c946ed56790f83580 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Tue, 17 Aug 2021 14:51:56 +0200 Subject: [PATCH 28/94] Small documentation update for the new metadata_collector --- clippy_lints/src/utils/conf.rs | 4 ---- .../src/utils/internal_lints/metadata_collector.rs | 2 +- doc/adding_lints.md | 7 ++++--- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 9ee2e3024520b..ad341fbbda904 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -31,9 +31,6 @@ impl TryConf { } } -/// Note that the configuration parsing currently doesn't support documentation that will -/// that spans over several lines. This will be possible with the new implementation -/// See (rust-clippy#7172) macro_rules! define_Conf { ($( $(#[doc = $doc:literal])+ @@ -130,7 +127,6 @@ macro_rules! define_Conf { }; } -// N.B., this macro is parsed by util/lintlib.py define_Conf! { /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_VEC, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX. /// diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index a48a53850830f..91533695eb309 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -82,7 +82,7 @@ This lint has the following configuration variables: /// `default` macro_rules! CONFIGURATION_VALUE_TEMPLATE { () => { - "* {name}: `{ty}`: {doc} (defaults to `{default}`)\n" + "* `{name}`: `{ty}`: {doc} (defaults to `{default}`)\n" }; } diff --git a/doc/adding_lints.md b/doc/adding_lints.md index f2260c3d1a267..392b70ee3a963 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -559,11 +559,12 @@ in the following steps: 1. Adding a new configuration entry to [clippy_utils::conf](/clippy_utils/src/conf.rs) like this: ```rust - /// Lint: LINT_NAME. + /// Lint: LINT_NAME. + /// + /// (configuration_ident: Type = DefaultValue), ``` - The configuration value and identifier should usually be the same. The doc comment will be - automatically added to the lint documentation. + The doc comment will be automatically added to the lint documentation. 2. Adding the configuration value to the lint impl struct: 1. This first requires the definition of a lint impl struct. Lint impl structs are usually generated with the `declare_lint_pass!` macro. This struct needs to be defined manually From 4123fedac5e0315a7f462d870d2ce2c5563d07e1 Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Fri, 6 Aug 2021 17:14:27 +0200 Subject: [PATCH 29/94] remove box_syntax uses from cranelift and tools --- clippy_lints/src/booleans.rs | 2 +- clippy_lints/src/doc.rs | 4 +- clippy_lints/src/lib.rs | 489 +++++++++++++++++------------------ 3 files changed, 247 insertions(+), 248 deletions(-) diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 4a83d35a568c5..6f12d34e66b6e 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -116,7 +116,7 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> { // prevent folding of `cfg!` macros and the like if !e.span.from_expansion() { match &e.kind { - ExprKind::Unary(UnOp::Not, inner) => return Ok(Bool::Not(box self.run(inner)?)), + ExprKind::Unary(UnOp::Not, inner) => return Ok(Bool::Not(Box::new(self.run(inner)?))), ExprKind::Binary(binop, lhs, rhs) => match &binop.node { BinOpKind::Or => { return Ok(Bool::Or(self.extract(BinOpKind::Or, &[lhs, rhs], Vec::new())?)); diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 75561cfde369b..cb2b7f5be70ad 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -578,8 +578,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 emitter = EmitterWriter::new(box io::sink(), None, false, false, false, None, false); - let handler = Handler::with_emitter(false, None, box emitter); + let emitter = EmitterWriter::new(Box::new(io::sink()), None, false, false, false, None, false); + let handler = Handler::with_emitter(false, None, Box::new(emitter)); let sess = ParseSess::with_span_handler(handler, sm); let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) { diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6f73a00d1f782..19719502870bd 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1,7 +1,6 @@ // error-pattern:cargo-clippy #![feature(box_patterns)] -#![feature(box_syntax)] #![feature(drain_filter)] #![feature(in_band_lifetimes)] #![feature(iter_zip)] @@ -393,9 +392,9 @@ use crate::utils::conf::TryConf; /// Used in `./src/driver.rs`. pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore) { // NOTE: Do not add any more pre-expansion passes. These should be removed eventually. - store.register_pre_expansion_pass(|| box write::Write::default()); - store.register_pre_expansion_pass(|| box attrs::EarlyAttributes); - store.register_pre_expansion_pass(|| box dbg_macro::DbgMacro); + store.register_pre_expansion_pass(|| Box::new(write::Write::default())); + store.register_pre_expansion_pass(|| Box::new(attrs::EarlyAttributes)); + store.register_pre_expansion_pass(|| Box::new(dbg_macro::DbgMacro)); } #[doc(hidden)] @@ -1810,7 +1809,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: #[cfg(feature = "metadata-collector-lint")] { if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) { - store.register_late_pass(|| box utils::internal_lints::metadata_collector::MetadataCollector::new()); + store.register_late_pass(|| Box::new(utils::internal_lints::metadata_collector::MetadataCollector::new())); return; } } @@ -1818,57 +1817,57 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: // all the internal lints #[cfg(feature = "internal-lints")] { - store.register_early_pass(|| box utils::internal_lints::ClippyLintsInternal); - store.register_early_pass(|| box utils::internal_lints::ProduceIce); - store.register_late_pass(|| box utils::inspector::DeepCodeInspector); - store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls); - store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); - store.register_late_pass(|| box utils::internal_lints::IfChainStyle); - store.register_late_pass(|| box utils::internal_lints::InvalidPaths); - store.register_late_pass(|| box utils::internal_lints::InterningDefinedSymbol::default()); - store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); - store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); - store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass); + store.register_early_pass(|| Box::new(utils::internal_lints::ClippyLintsInternal)); + store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce)); + store.register_late_pass(|| Box::new(utils::inspector::DeepCodeInspector)); + store.register_late_pass(|| Box::new(utils::internal_lints::CollapsibleCalls)); + store.register_late_pass(|| Box::new(utils::internal_lints::CompilerLintFunctions::new())); + store.register_late_pass(|| Box::new(utils::internal_lints::IfChainStyle)); + store.register_late_pass(|| Box::new(utils::internal_lints::InvalidPaths)); + store.register_late_pass(|| Box::new(utils::internal_lints::InterningDefinedSymbol::default())); + store.register_late_pass(|| Box::new(utils::internal_lints::LintWithoutLintPass::default())); + store.register_late_pass(|| Box::new(utils::internal_lints::MatchTypeOnDiagItem)); + store.register_late_pass(|| Box::new(utils::internal_lints::OuterExpnDataPass)); } - store.register_late_pass(|| box utils::author::Author); - store.register_late_pass(|| box await_holding_invalid::AwaitHolding); - store.register_late_pass(|| box serde_api::SerdeApi); + store.register_late_pass(|| Box::new(utils::author::Author)); + store.register_late_pass(|| Box::new(await_holding_invalid::AwaitHolding)); + store.register_late_pass(|| Box::new(serde_api::SerdeApi)); let vec_box_size_threshold = conf.vec_box_size_threshold; let type_complexity_threshold = conf.type_complexity_threshold; - store.register_late_pass(move || box types::Types::new(vec_box_size_threshold, type_complexity_threshold)); - store.register_late_pass(|| box booleans::NonminimalBool); - store.register_late_pass(|| box needless_bitwise_bool::NeedlessBitwiseBool); - store.register_late_pass(|| box eq_op::EqOp); - store.register_late_pass(|| box enum_clike::UnportableVariant); - store.register_late_pass(|| box float_literal::FloatLiteral); + store.register_late_pass(move || Box::new(types::Types::new(vec_box_size_threshold, type_complexity_threshold))); + store.register_late_pass(|| Box::new(booleans::NonminimalBool)); + store.register_late_pass(|| Box::new(needless_bitwise_bool::NeedlessBitwiseBool)); + store.register_late_pass(|| Box::new(eq_op::EqOp)); + store.register_late_pass(|| Box::new(enum_clike::UnportableVariant)); + store.register_late_pass(|| Box::new(float_literal::FloatLiteral)); let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold; - store.register_late_pass(move || box bit_mask::BitMask::new(verbose_bit_mask_threshold)); - store.register_late_pass(|| box ptr::Ptr); - store.register_late_pass(|| box ptr_eq::PtrEq); - store.register_late_pass(|| box needless_bool::NeedlessBool); - store.register_late_pass(|| box needless_bool::BoolComparison); - store.register_late_pass(|| box needless_for_each::NeedlessForEach); - store.register_late_pass(|| box approx_const::ApproxConstant); - store.register_late_pass(|| box misc::MiscLints); - store.register_late_pass(|| box eta_reduction::EtaReduction); - store.register_late_pass(|| box identity_op::IdentityOp); - store.register_late_pass(|| box erasing_op::ErasingOp); - store.register_late_pass(|| box mut_mut::MutMut); - store.register_late_pass(|| box mut_reference::UnnecessaryMutPassed); - store.register_late_pass(|| box len_zero::LenZero); - store.register_late_pass(|| box attrs::Attributes); - store.register_late_pass(|| box blocks_in_if_conditions::BlocksInIfConditions); - store.register_late_pass(|| box collapsible_match::CollapsibleMatch); - store.register_late_pass(|| box unicode::Unicode); - store.register_late_pass(|| box unit_return_expecting_ord::UnitReturnExpectingOrd); - store.register_late_pass(|| box strings::StringAdd); - store.register_late_pass(|| box implicit_return::ImplicitReturn); - store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub); - store.register_late_pass(|| box default_numeric_fallback::DefaultNumericFallback); - store.register_late_pass(|| box inconsistent_struct_constructor::InconsistentStructConstructor); - store.register_late_pass(|| box non_octal_unix_permissions::NonOctalUnixPermissions); - store.register_early_pass(|| box unnecessary_self_imports::UnnecessarySelfImports); + store.register_late_pass(move || Box::new(bit_mask::BitMask::new(verbose_bit_mask_threshold))); + store.register_late_pass(|| Box::new(ptr::Ptr)); + store.register_late_pass(|| Box::new(ptr_eq::PtrEq)); + store.register_late_pass(|| Box::new(needless_bool::NeedlessBool)); + store.register_late_pass(|| Box::new(needless_bool::BoolComparison)); + store.register_late_pass(|| Box::new(needless_for_each::NeedlessForEach)); + store.register_late_pass(|| Box::new(approx_const::ApproxConstant)); + store.register_late_pass(|| Box::new(misc::MiscLints)); + store.register_late_pass(|| Box::new(eta_reduction::EtaReduction)); + store.register_late_pass(|| Box::new(identity_op::IdentityOp)); + store.register_late_pass(|| Box::new(erasing_op::ErasingOp)); + store.register_late_pass(|| Box::new(mut_mut::MutMut)); + store.register_late_pass(|| Box::new(mut_reference::UnnecessaryMutPassed)); + store.register_late_pass(|| Box::new(len_zero::LenZero)); + store.register_late_pass(|| Box::new(attrs::Attributes)); + store.register_late_pass(|| Box::new(blocks_in_if_conditions::BlocksInIfConditions)); + store.register_late_pass(|| Box::new(collapsible_match::CollapsibleMatch)); + store.register_late_pass(|| Box::new(unicode::Unicode)); + store.register_late_pass(|| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd)); + store.register_late_pass(|| Box::new(strings::StringAdd)); + store.register_late_pass(|| Box::new(implicit_return::ImplicitReturn)); + store.register_late_pass(|| Box::new(implicit_saturating_sub::ImplicitSaturatingSub)); + store.register_late_pass(|| Box::new(default_numeric_fallback::DefaultNumericFallback)); + store.register_late_pass(|| Box::new(inconsistent_struct_constructor::InconsistentStructConstructor)); + store.register_late_pass(|| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions)); + store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports)); let msrv = conf.msrv.as_ref().and_then(|s| { parse_msrv(s, None, None).or_else(|| { @@ -1878,231 +1877,231 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: }); let avoid_breaking_exported_api = conf.avoid_breaking_exported_api; - store.register_late_pass(move || box methods::Methods::new(avoid_breaking_exported_api, msrv)); - store.register_late_pass(move || box matches::Matches::new(msrv)); - store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(msrv)); - store.register_late_pass(move || box manual_strip::ManualStrip::new(msrv)); - store.register_early_pass(move || box redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv)); - store.register_early_pass(move || box redundant_field_names::RedundantFieldNames::new(msrv)); - store.register_late_pass(move || box checked_conversions::CheckedConversions::new(msrv)); - store.register_late_pass(move || box mem_replace::MemReplace::new(msrv)); - store.register_late_pass(move || box ranges::Ranges::new(msrv)); - store.register_late_pass(move || box from_over_into::FromOverInto::new(msrv)); - store.register_late_pass(move || box use_self::UseSelf::new(msrv)); - store.register_late_pass(move || box missing_const_for_fn::MissingConstForFn::new(msrv)); - store.register_late_pass(move || box needless_question_mark::NeedlessQuestionMark); - store.register_late_pass(move || box casts::Casts::new(msrv)); - store.register_early_pass(move || box unnested_or_patterns::UnnestedOrPatterns::new(msrv)); + store.register_late_pass(move || Box::new(methods::Methods::new(avoid_breaking_exported_api, msrv))); + store.register_late_pass(move || Box::new(matches::Matches::new(msrv))); + store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustive::new(msrv))); + store.register_late_pass(move || Box::new(manual_strip::ManualStrip::new(msrv))); + store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv))); + store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv))); + store.register_late_pass(move || Box::new(checked_conversions::CheckedConversions::new(msrv))); + store.register_late_pass(move || Box::new(mem_replace::MemReplace::new(msrv))); + store.register_late_pass(move || Box::new(ranges::Ranges::new(msrv))); + store.register_late_pass(move || Box::new(from_over_into::FromOverInto::new(msrv))); + store.register_late_pass(move || Box::new(use_self::UseSelf::new(msrv))); + store.register_late_pass(move || Box::new(missing_const_for_fn::MissingConstForFn::new(msrv))); + store.register_late_pass(move || Box::new(needless_question_mark::NeedlessQuestionMark)); + store.register_late_pass(move || Box::new(casts::Casts::new(msrv))); + store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv))); - store.register_late_pass(|| box size_of_in_element_count::SizeOfInElementCount); - store.register_late_pass(|| box map_clone::MapClone); - store.register_late_pass(|| box map_err_ignore::MapErrIgnore); - store.register_late_pass(|| box shadow::Shadow); - store.register_late_pass(|| box unit_types::UnitTypes); - store.register_late_pass(|| box loops::Loops); - store.register_late_pass(|| box main_recursion::MainRecursion::default()); - store.register_late_pass(|| box lifetimes::Lifetimes); - store.register_late_pass(|| box entry::HashMapPass); - store.register_late_pass(|| box minmax::MinMaxPass); - store.register_late_pass(|| box open_options::OpenOptions); - store.register_late_pass(|| box zero_div_zero::ZeroDiv); - store.register_late_pass(|| box mutex_atomic::Mutex); - store.register_late_pass(|| box needless_update::NeedlessUpdate); - store.register_late_pass(|| box needless_borrow::NeedlessBorrow::default()); - store.register_late_pass(|| box needless_borrowed_ref::NeedlessBorrowedRef); - store.register_late_pass(|| box no_effect::NoEffect); - store.register_late_pass(|| box temporary_assignment::TemporaryAssignment); - store.register_late_pass(|| box transmute::Transmute); + store.register_late_pass(|| Box::new(size_of_in_element_count::SizeOfInElementCount)); + store.register_late_pass(|| Box::new(map_clone::MapClone)); + store.register_late_pass(|| Box::new(map_err_ignore::MapErrIgnore)); + store.register_late_pass(|| Box::new(shadow::Shadow)); + store.register_late_pass(|| Box::new(unit_types::UnitTypes)); + store.register_late_pass(|| Box::new(loops::Loops)); + store.register_late_pass(|| Box::new(main_recursion::MainRecursion::default())); + store.register_late_pass(|| Box::new(lifetimes::Lifetimes)); + store.register_late_pass(|| Box::new(entry::HashMapPass)); + store.register_late_pass(|| Box::new(minmax::MinMaxPass)); + store.register_late_pass(|| Box::new(open_options::OpenOptions)); + store.register_late_pass(|| Box::new(zero_div_zero::ZeroDiv)); + store.register_late_pass(|| Box::new(mutex_atomic::Mutex)); + store.register_late_pass(|| Box::new(needless_update::NeedlessUpdate)); + store.register_late_pass(|| Box::new(needless_borrow::NeedlessBorrow::default())); + store.register_late_pass(|| Box::new(needless_borrowed_ref::NeedlessBorrowedRef)); + store.register_late_pass(|| Box::new(no_effect::NoEffect)); + store.register_late_pass(|| Box::new(temporary_assignment::TemporaryAssignment)); + store.register_late_pass(|| Box::new(transmute::Transmute)); let cognitive_complexity_threshold = conf.cognitive_complexity_threshold; - store.register_late_pass(move || box cognitive_complexity::CognitiveComplexity::new(cognitive_complexity_threshold)); + store.register_late_pass(move || Box::new(cognitive_complexity::CognitiveComplexity::new(cognitive_complexity_threshold))); let too_large_for_stack = conf.too_large_for_stack; - store.register_late_pass(move || box escape::BoxedLocal{too_large_for_stack}); - store.register_late_pass(move || box vec::UselessVec{too_large_for_stack}); - store.register_late_pass(|| box panic_unimplemented::PanicUnimplemented); - store.register_late_pass(|| box strings::StringLitAsBytes); - store.register_late_pass(|| box derive::Derive); - store.register_late_pass(|| box get_last_with_len::GetLastWithLen); - store.register_late_pass(|| box drop_forget_ref::DropForgetRef); - store.register_late_pass(|| box empty_enum::EmptyEnum); - store.register_late_pass(|| box absurd_extreme_comparisons::AbsurdExtremeComparisons); - store.register_late_pass(|| box invalid_upcast_comparisons::InvalidUpcastComparisons); - store.register_late_pass(|| box regex::Regex::default()); - store.register_late_pass(|| box copies::CopyAndPaste); - store.register_late_pass(|| box copy_iterator::CopyIterator); - store.register_late_pass(|| box format::UselessFormat); - store.register_late_pass(|| box swap::Swap); - store.register_late_pass(|| box overflow_check_conditional::OverflowCheckConditional); - store.register_late_pass(|| box new_without_default::NewWithoutDefault::default()); + store.register_late_pass(move || Box::new(escape::BoxedLocal{too_large_for_stack})); + store.register_late_pass(move || Box::new(vec::UselessVec{too_large_for_stack})); + store.register_late_pass(|| Box::new(panic_unimplemented::PanicUnimplemented)); + store.register_late_pass(|| Box::new(strings::StringLitAsBytes)); + store.register_late_pass(|| Box::new(derive::Derive)); + store.register_late_pass(|| Box::new(get_last_with_len::GetLastWithLen)); + store.register_late_pass(|| Box::new(drop_forget_ref::DropForgetRef)); + store.register_late_pass(|| Box::new(empty_enum::EmptyEnum)); + store.register_late_pass(|| Box::new(absurd_extreme_comparisons::AbsurdExtremeComparisons)); + store.register_late_pass(|| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons)); + store.register_late_pass(|| Box::new(regex::Regex::default())); + store.register_late_pass(|| Box::new(copies::CopyAndPaste)); + store.register_late_pass(|| Box::new(copy_iterator::CopyIterator)); + store.register_late_pass(|| Box::new(format::UselessFormat)); + store.register_late_pass(|| Box::new(swap::Swap)); + store.register_late_pass(|| Box::new(overflow_check_conditional::OverflowCheckConditional)); + store.register_late_pass(|| Box::new(new_without_default::NewWithoutDefault::default())); let blacklisted_names = conf.blacklisted_names.iter().cloned().collect::>(); - store.register_late_pass(move || box blacklisted_name::BlacklistedName::new(blacklisted_names.clone())); + store.register_late_pass(move || Box::new(blacklisted_name::BlacklistedName::new(blacklisted_names.clone()))); let too_many_arguments_threshold = conf.too_many_arguments_threshold; let too_many_lines_threshold = conf.too_many_lines_threshold; - store.register_late_pass(move || box functions::Functions::new(too_many_arguments_threshold, too_many_lines_threshold)); + store.register_late_pass(move || Box::new(functions::Functions::new(too_many_arguments_threshold, too_many_lines_threshold))); let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::>(); - store.register_late_pass(move || box doc::DocMarkdown::new(doc_valid_idents.clone())); - store.register_late_pass(|| box neg_multiply::NegMultiply); - store.register_late_pass(|| box mem_discriminant::MemDiscriminant); - store.register_late_pass(|| box mem_forget::MemForget); - store.register_late_pass(|| box arithmetic::Arithmetic::default()); - store.register_late_pass(|| box assign_ops::AssignOps); - store.register_late_pass(|| box let_if_seq::LetIfSeq); - store.register_late_pass(|| box eval_order_dependence::EvalOrderDependence); - store.register_late_pass(|| box missing_doc::MissingDoc::new()); - store.register_late_pass(|| box missing_inline::MissingInline); - store.register_late_pass(move || box exhaustive_items::ExhaustiveItems); - store.register_late_pass(|| box if_let_some_result::OkIfLet); - store.register_late_pass(|| box partialeq_ne_impl::PartialEqNeImpl); - store.register_late_pass(|| box unused_io_amount::UnusedIoAmount); + store.register_late_pass(move || Box::new(doc::DocMarkdown::new(doc_valid_idents.clone()))); + store.register_late_pass(|| Box::new(neg_multiply::NegMultiply)); + store.register_late_pass(|| Box::new(mem_discriminant::MemDiscriminant)); + store.register_late_pass(|| Box::new(mem_forget::MemForget)); + store.register_late_pass(|| Box::new(arithmetic::Arithmetic::default())); + store.register_late_pass(|| Box::new(assign_ops::AssignOps)); + store.register_late_pass(|| Box::new(let_if_seq::LetIfSeq)); + store.register_late_pass(|| Box::new(eval_order_dependence::EvalOrderDependence)); + store.register_late_pass(|| Box::new(missing_doc::MissingDoc::new())); + store.register_late_pass(|| Box::new(missing_inline::MissingInline)); + store.register_late_pass(move || Box::new(exhaustive_items::ExhaustiveItems)); + store.register_late_pass(|| Box::new(if_let_some_result::OkIfLet)); + store.register_late_pass(|| Box::new(partialeq_ne_impl::PartialEqNeImpl)); + store.register_late_pass(|| Box::new(unused_io_amount::UnusedIoAmount)); let enum_variant_size_threshold = conf.enum_variant_size_threshold; - store.register_late_pass(move || box large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold)); - store.register_late_pass(|| box explicit_write::ExplicitWrite); - store.register_late_pass(|| box needless_pass_by_value::NeedlessPassByValue); + store.register_late_pass(move || Box::new(large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold))); + store.register_late_pass(|| Box::new(explicit_write::ExplicitWrite)); + store.register_late_pass(|| Box::new(needless_pass_by_value::NeedlessPassByValue)); let pass_by_ref_or_value = pass_by_ref_or_value::PassByRefOrValue::new( conf.trivial_copy_size_limit, conf.pass_by_value_size_limit, conf.avoid_breaking_exported_api, &sess.target, ); - store.register_late_pass(move || box pass_by_ref_or_value); - store.register_late_pass(|| box ref_option_ref::RefOptionRef); - store.register_late_pass(|| box try_err::TryErr); - store.register_late_pass(|| box bytecount::ByteCount); - store.register_late_pass(|| box infinite_iter::InfiniteIter); - store.register_late_pass(|| box inline_fn_without_body::InlineFnWithoutBody); - store.register_late_pass(|| box useless_conversion::UselessConversion::default()); - store.register_late_pass(|| box implicit_hasher::ImplicitHasher); - store.register_late_pass(|| box fallible_impl_from::FallibleImplFrom); - store.register_late_pass(|| box double_comparison::DoubleComparisons); - store.register_late_pass(|| box question_mark::QuestionMark); - store.register_early_pass(|| box suspicious_operation_groupings::SuspiciousOperationGroupings); - store.register_late_pass(|| box suspicious_trait_impl::SuspiciousImpl); - store.register_late_pass(|| box map_unit_fn::MapUnit); - store.register_late_pass(|| box inherent_impl::MultipleInherentImpl); - store.register_late_pass(|| box neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd); - store.register_late_pass(|| box unwrap::Unwrap); - store.register_late_pass(|| box duration_subsec::DurationSubsec); - store.register_late_pass(|| box indexing_slicing::IndexingSlicing); - store.register_late_pass(|| box non_copy_const::NonCopyConst); - store.register_late_pass(|| box ptr_offset_with_cast::PtrOffsetWithCast); - store.register_late_pass(|| box redundant_clone::RedundantClone); - store.register_late_pass(|| box slow_vector_initialization::SlowVectorInit); - store.register_late_pass(|| box unnecessary_sort_by::UnnecessarySortBy); - store.register_late_pass(move || box unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api)); - store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants); - store.register_late_pass(|| box transmuting_null::TransmutingNull); - store.register_late_pass(|| box path_buf_push_overwrite::PathBufPushOverwrite); - store.register_late_pass(|| box integer_division::IntegerDivision); - store.register_late_pass(|| box inherent_to_string::InherentToString); + store.register_late_pass(move || Box::new(pass_by_ref_or_value)); + store.register_late_pass(|| Box::new(ref_option_ref::RefOptionRef)); + store.register_late_pass(|| Box::new(try_err::TryErr)); + store.register_late_pass(|| Box::new(bytecount::ByteCount)); + store.register_late_pass(|| Box::new(infinite_iter::InfiniteIter)); + store.register_late_pass(|| Box::new(inline_fn_without_body::InlineFnWithoutBody)); + store.register_late_pass(|| Box::new(useless_conversion::UselessConversion::default())); + store.register_late_pass(|| Box::new(implicit_hasher::ImplicitHasher)); + store.register_late_pass(|| Box::new(fallible_impl_from::FallibleImplFrom)); + store.register_late_pass(|| Box::new(double_comparison::DoubleComparisons)); + store.register_late_pass(|| Box::new(question_mark::QuestionMark)); + store.register_early_pass(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings)); + store.register_late_pass(|| Box::new(suspicious_trait_impl::SuspiciousImpl)); + store.register_late_pass(|| Box::new(map_unit_fn::MapUnit)); + store.register_late_pass(|| Box::new(inherent_impl::MultipleInherentImpl)); + store.register_late_pass(|| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd)); + store.register_late_pass(|| Box::new(unwrap::Unwrap)); + store.register_late_pass(|| Box::new(duration_subsec::DurationSubsec)); + store.register_late_pass(|| Box::new(indexing_slicing::IndexingSlicing)); + store.register_late_pass(|| Box::new(non_copy_const::NonCopyConst)); + store.register_late_pass(|| Box::new(ptr_offset_with_cast::PtrOffsetWithCast)); + store.register_late_pass(|| Box::new(redundant_clone::RedundantClone)); + store.register_late_pass(|| Box::new(slow_vector_initialization::SlowVectorInit)); + store.register_late_pass(|| Box::new(unnecessary_sort_by::UnnecessarySortBy)); + store.register_late_pass(move || Box::new(unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api))); + store.register_late_pass(|| Box::new(assertions_on_constants::AssertionsOnConstants)); + store.register_late_pass(|| Box::new(transmuting_null::TransmutingNull)); + store.register_late_pass(|| Box::new(path_buf_push_overwrite::PathBufPushOverwrite)); + store.register_late_pass(|| Box::new(integer_division::IntegerDivision)); + store.register_late_pass(|| Box::new(inherent_to_string::InherentToString)); let max_trait_bounds = conf.max_trait_bounds; - store.register_late_pass(move || box trait_bounds::TraitBounds::new(max_trait_bounds)); - store.register_late_pass(|| box comparison_chain::ComparisonChain); - store.register_late_pass(|| box mut_key::MutableKeyType); - store.register_late_pass(|| box modulo_arithmetic::ModuloArithmetic); - store.register_early_pass(|| box reference::DerefAddrOf); - store.register_early_pass(|| box reference::RefInDeref); - store.register_early_pass(|| box double_parens::DoubleParens); - store.register_late_pass(|| box to_string_in_display::ToStringInDisplay::new()); - store.register_early_pass(|| box unsafe_removed_from_name::UnsafeNameRemoval); - store.register_early_pass(|| box if_not_else::IfNotElse); - store.register_early_pass(|| box else_if_without_else::ElseIfWithoutElse); - store.register_early_pass(|| box int_plus_one::IntPlusOne); - store.register_early_pass(|| box formatting::Formatting); - store.register_early_pass(|| box misc_early::MiscEarlyLints); - store.register_early_pass(|| box redundant_closure_call::RedundantClosureCall); - store.register_late_pass(|| box redundant_closure_call::RedundantClosureCall); - store.register_early_pass(|| box unused_unit::UnusedUnit); - store.register_late_pass(|| box returns::Return); - store.register_early_pass(|| box collapsible_if::CollapsibleIf); - store.register_early_pass(|| box items_after_statements::ItemsAfterStatements); - store.register_early_pass(|| box precedence::Precedence); - store.register_early_pass(|| box needless_continue::NeedlessContinue); - store.register_early_pass(|| box redundant_else::RedundantElse); - store.register_late_pass(|| box create_dir::CreateDir); - store.register_early_pass(|| box needless_arbitrary_self_type::NeedlessArbitrarySelfType); + store.register_late_pass(move || Box::new(trait_bounds::TraitBounds::new(max_trait_bounds))); + store.register_late_pass(|| Box::new(comparison_chain::ComparisonChain)); + store.register_late_pass(|| Box::new(mut_key::MutableKeyType)); + store.register_late_pass(|| Box::new(modulo_arithmetic::ModuloArithmetic)); + store.register_early_pass(|| Box::new(reference::DerefAddrOf)); + store.register_early_pass(|| Box::new(reference::RefInDeref)); + store.register_early_pass(|| Box::new(double_parens::DoubleParens)); + store.register_late_pass(|| Box::new(to_string_in_display::ToStringInDisplay::new())); + store.register_early_pass(|| Box::new(unsafe_removed_from_name::UnsafeNameRemoval)); + store.register_early_pass(|| Box::new(if_not_else::IfNotElse)); + store.register_early_pass(|| Box::new(else_if_without_else::ElseIfWithoutElse)); + store.register_early_pass(|| Box::new(int_plus_one::IntPlusOne)); + store.register_early_pass(|| Box::new(formatting::Formatting)); + store.register_early_pass(|| Box::new(misc_early::MiscEarlyLints)); + store.register_early_pass(|| Box::new(redundant_closure_call::RedundantClosureCall)); + store.register_late_pass(|| Box::new(redundant_closure_call::RedundantClosureCall)); + store.register_early_pass(|| Box::new(unused_unit::UnusedUnit)); + store.register_late_pass(|| Box::new(returns::Return)); + store.register_early_pass(|| Box::new(collapsible_if::CollapsibleIf)); + store.register_early_pass(|| Box::new(items_after_statements::ItemsAfterStatements)); + store.register_early_pass(|| Box::new(precedence::Precedence)); + store.register_early_pass(|| Box::new(needless_continue::NeedlessContinue)); + store.register_early_pass(|| Box::new(redundant_else::RedundantElse)); + store.register_late_pass(|| Box::new(create_dir::CreateDir)); + store.register_early_pass(|| Box::new(needless_arbitrary_self_type::NeedlessArbitrarySelfType)); let cargo_ignore_publish = conf.cargo_ignore_publish; - store.register_late_pass(move || box cargo_common_metadata::CargoCommonMetadata::new(cargo_ignore_publish)); - store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions); - store.register_late_pass(|| box wildcard_dependencies::WildcardDependencies); + store.register_late_pass(move || Box::new(cargo_common_metadata::CargoCommonMetadata::new(cargo_ignore_publish))); + store.register_late_pass(|| Box::new(multiple_crate_versions::MultipleCrateVersions)); + store.register_late_pass(|| Box::new(wildcard_dependencies::WildcardDependencies)); let literal_representation_lint_fraction_readability = conf.unreadable_literal_lint_fractions; - store.register_early_pass(move || box literal_representation::LiteralDigitGrouping::new(literal_representation_lint_fraction_readability)); + store.register_early_pass(move || Box::new(literal_representation::LiteralDigitGrouping::new(literal_representation_lint_fraction_readability))); let literal_representation_threshold = conf.literal_representation_threshold; - store.register_early_pass(move || box literal_representation::DecimalLiteralRepresentation::new(literal_representation_threshold)); + store.register_early_pass(move || Box::new(literal_representation::DecimalLiteralRepresentation::new(literal_representation_threshold))); let enum_variant_name_threshold = conf.enum_variant_name_threshold; - store.register_late_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold, avoid_breaking_exported_api)); - store.register_early_pass(|| box tabs_in_doc_comments::TabsInDocComments); + store.register_late_pass(move || Box::new(enum_variants::EnumVariantNames::new(enum_variant_name_threshold, avoid_breaking_exported_api))); + store.register_early_pass(|| Box::new(tabs_in_doc_comments::TabsInDocComments)); let upper_case_acronyms_aggressive = conf.upper_case_acronyms_aggressive; - store.register_late_pass(move || box upper_case_acronyms::UpperCaseAcronyms::new(avoid_breaking_exported_api, upper_case_acronyms_aggressive)); - store.register_late_pass(|| box default::Default::default()); - store.register_late_pass(|| box unused_self::UnusedSelf); - store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall); - store.register_late_pass(|| box exit::Exit); - store.register_late_pass(|| box to_digit_is_some::ToDigitIsSome); + store.register_late_pass(move || Box::new(upper_case_acronyms::UpperCaseAcronyms::new(avoid_breaking_exported_api, upper_case_acronyms_aggressive))); + store.register_late_pass(|| Box::new(default::Default::default())); + store.register_late_pass(|| Box::new(unused_self::UnusedSelf)); + store.register_late_pass(|| Box::new(mutable_debug_assertion::DebugAssertWithMutCall)); + store.register_late_pass(|| Box::new(exit::Exit)); + store.register_late_pass(|| Box::new(to_digit_is_some::ToDigitIsSome)); let array_size_threshold = conf.array_size_threshold; - store.register_late_pass(move || box large_stack_arrays::LargeStackArrays::new(array_size_threshold)); - store.register_late_pass(move || box large_const_arrays::LargeConstArrays::new(array_size_threshold)); - store.register_late_pass(|| box floating_point_arithmetic::FloatingPointArithmetic); - store.register_early_pass(|| box as_conversions::AsConversions); - store.register_late_pass(|| box let_underscore::LetUnderscore); - store.register_early_pass(|| box single_component_path_imports::SingleComponentPathImports); + store.register_late_pass(move || Box::new(large_stack_arrays::LargeStackArrays::new(array_size_threshold))); + store.register_late_pass(move || Box::new(large_const_arrays::LargeConstArrays::new(array_size_threshold))); + store.register_late_pass(|| Box::new(floating_point_arithmetic::FloatingPointArithmetic)); + store.register_early_pass(|| Box::new(as_conversions::AsConversions)); + store.register_late_pass(|| Box::new(let_underscore::LetUnderscore)); + store.register_early_pass(|| Box::new(single_component_path_imports::SingleComponentPathImports)); let max_fn_params_bools = conf.max_fn_params_bools; let max_struct_bools = conf.max_struct_bools; - store.register_early_pass(move || box excessive_bools::ExcessiveBools::new(max_struct_bools, max_fn_params_bools)); - store.register_early_pass(|| box option_env_unwrap::OptionEnvUnwrap); + store.register_early_pass(move || Box::new(excessive_bools::ExcessiveBools::new(max_struct_bools, max_fn_params_bools))); + store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap)); let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports; - store.register_late_pass(move || box wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)); - store.register_late_pass(|| box verbose_file_reads::VerboseFileReads); - store.register_late_pass(|| box redundant_pub_crate::RedundantPubCrate::default()); - store.register_late_pass(|| box unnamed_address::UnnamedAddress); - store.register_late_pass(|| box dereference::Dereferencing::default()); - store.register_late_pass(|| box option_if_let_else::OptionIfLetElse); - store.register_late_pass(|| box future_not_send::FutureNotSend); - store.register_late_pass(|| box if_let_mutex::IfLetMutex); - store.register_late_pass(|| box mut_mutex_lock::MutMutexLock); - store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems); - store.register_late_pass(|| box manual_async_fn::ManualAsyncFn); - store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero); - store.register_late_pass(|| box panic_in_result_fn::PanicInResultFn); + store.register_late_pass(move || Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports))); + store.register_late_pass(|| Box::new(verbose_file_reads::VerboseFileReads)); + store.register_late_pass(|| Box::new(redundant_pub_crate::RedundantPubCrate::default())); + store.register_late_pass(|| Box::new(unnamed_address::UnnamedAddress)); + store.register_late_pass(|| Box::new(dereference::Dereferencing::default())); + store.register_late_pass(|| Box::new(option_if_let_else::OptionIfLetElse)); + store.register_late_pass(|| Box::new(future_not_send::FutureNotSend)); + store.register_late_pass(|| Box::new(if_let_mutex::IfLetMutex)); + store.register_late_pass(|| Box::new(mut_mutex_lock::MutMutexLock)); + store.register_late_pass(|| Box::new(match_on_vec_items::MatchOnVecItems)); + store.register_late_pass(|| Box::new(manual_async_fn::ManualAsyncFn)); + store.register_late_pass(|| Box::new(vec_resize_to_zero::VecResizeToZero)); + store.register_late_pass(|| Box::new(panic_in_result_fn::PanicInResultFn)); let single_char_binding_names_threshold = conf.single_char_binding_names_threshold; - store.register_early_pass(move || box non_expressive_names::NonExpressiveNames { + store.register_early_pass(move || Box::new(non_expressive_names::NonExpressiveNames { single_char_binding_names_threshold, - }); + })); let macro_matcher = conf.standard_macro_braces.iter().cloned().collect::>(); - store.register_early_pass(move || box nonstandard_macro_braces::MacroBraces::new(¯o_matcher)); - store.register_late_pass(|| box macro_use::MacroUseImports::default()); - store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch); - store.register_late_pass(|| box stable_sort_primitive::StableSortPrimitive); - store.register_late_pass(|| box repeat_once::RepeatOnce); - store.register_late_pass(|| box unwrap_in_result::UnwrapInResult); - store.register_late_pass(|| box self_assignment::SelfAssignment); - store.register_late_pass(|| box manual_unwrap_or::ManualUnwrapOr); - store.register_late_pass(|| box manual_ok_or::ManualOkOr); - store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); - store.register_late_pass(|| box semicolon_if_nothing_returned::SemicolonIfNothingReturned); - store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); + store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(¯o_matcher))); + store.register_late_pass(|| Box::new(macro_use::MacroUseImports::default())); + store.register_late_pass(|| Box::new(pattern_type_mismatch::PatternTypeMismatch)); + store.register_late_pass(|| Box::new(stable_sort_primitive::StableSortPrimitive)); + store.register_late_pass(|| Box::new(repeat_once::RepeatOnce)); + store.register_late_pass(|| Box::new(unwrap_in_result::UnwrapInResult)); + store.register_late_pass(|| Box::new(self_assignment::SelfAssignment)); + store.register_late_pass(|| Box::new(manual_unwrap_or::ManualUnwrapOr)); + store.register_late_pass(|| Box::new(manual_ok_or::ManualOkOr)); + store.register_late_pass(|| Box::new(float_equality_without_abs::FloatEqualityWithoutAbs)); + store.register_late_pass(|| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned)); + store.register_late_pass(|| Box::new(async_yields_async::AsyncYieldsAsync)); let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::>(); - store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); - store.register_early_pass(|| box asm_syntax::InlineAsmX86AttSyntax); - store.register_early_pass(|| box asm_syntax::InlineAsmX86IntelSyntax); - store.register_late_pass(|| box undropped_manually_drops::UndroppedManuallyDrops); - store.register_late_pass(|| box strings::StrToString); - store.register_late_pass(|| box strings::StringToString); - store.register_late_pass(|| box zero_sized_map_values::ZeroSizedMapValues); - store.register_late_pass(|| box vec_init_then_push::VecInitThenPush::default()); - store.register_late_pass(|| box case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons); - store.register_late_pass(|| box redundant_slicing::RedundantSlicing); - store.register_late_pass(|| box from_str_radix_10::FromStrRadix10); - store.register_late_pass(|| box manual_map::ManualMap); - store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv)); - store.register_early_pass(|| box bool_assert_comparison::BoolAssertComparison); - store.register_late_pass(|| box unused_async::UnusedAsync); + store.register_late_pass(move || Box::new(disallowed_method::DisallowedMethod::new(&disallowed_methods))); + store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax)); + store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax)); + store.register_late_pass(|| Box::new(undropped_manually_drops::UndroppedManuallyDrops)); + store.register_late_pass(|| Box::new(strings::StrToString)); + store.register_late_pass(|| Box::new(strings::StringToString)); + store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues)); + store.register_late_pass(|| Box::new(vec_init_then_push::VecInitThenPush::default())); + store.register_late_pass(|| Box::new(case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons)); + store.register_late_pass(|| Box::new(redundant_slicing::RedundantSlicing)); + store.register_late_pass(|| Box::new(from_str_radix_10::FromStrRadix10)); + store.register_late_pass(|| Box::new(manual_map::ManualMap)); + store.register_late_pass(move || Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv))); + store.register_early_pass(|| Box::new(bool_assert_comparison::BoolAssertComparison)); + store.register_late_pass(|| Box::new(unused_async::UnusedAsync)); let disallowed_types = conf.disallowed_types.iter().cloned().collect::>(); - store.register_late_pass(move || box disallowed_type::DisallowedType::new(&disallowed_types)); + store.register_late_pass(move || Box::new(disallowed_type::DisallowedType::new(&disallowed_types))); let import_renames = conf.enforced_import_renames.clone(); - store.register_late_pass(move || box missing_enforced_import_rename::ImportRename::new(import_renames.clone())); + store.register_late_pass(move || Box::new(missing_enforced_import_rename::ImportRename::new(import_renames.clone()))); let scripts = conf.allowed_scripts.clone(); - store.register_early_pass(move || box disallowed_script_idents::DisallowedScriptIdents::new(&scripts)); - store.register_late_pass(|| box strlen_on_c_strings::StrlenOnCStrings); - store.register_late_pass(move || box self_named_constructors::SelfNamedConstructors); + store.register_early_pass(move || Box::new(disallowed_script_idents::DisallowedScriptIdents::new(&scripts))); + store.register_late_pass(|| Box::new(strlen_on_c_strings::StrlenOnCStrings)); + store.register_late_pass(move || Box::new(self_named_constructors::SelfNamedConstructors)); } #[rustfmt::skip] From ae02282ad0296d868a1e7d63d605834e311bb80f Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 19 Aug 2021 13:31:25 -0500 Subject: [PATCH 30/94] Fix clippy let expressions fallout --- clippy_lints/src/collapsible_match.rs | 157 +++++++++++------------ clippy_lints/src/if_let_mutex.rs | 2 +- clippy_lints/src/if_let_some_result.rs | 2 +- clippy_lints/src/loops/manual_flatten.rs | 2 +- clippy_lints/src/loops/while_let_loop.rs | 2 +- clippy_lints/src/manual_map.rs | 2 +- clippy_lints/src/matches.rs | 20 +-- clippy_lints/src/option_if_let_else.rs | 2 +- clippy_lints/src/question_mark.rs | 2 +- clippy_utils/src/higher.rs | 34 ++--- clippy_utils/src/lib.rs | 4 + tests/ui/collapsible_match.rs | 5 + tests/ui/collapsible_match.stderr | 20 +-- tests/ui/collapsible_match2.stderr | 10 +- tests/ui/if_let_some_result.fixed | 9 +- tests/ui/if_let_some_result.rs | 9 +- tests/ui/if_let_some_result.stderr | 4 +- 17 files changed, 136 insertions(+), 150 deletions(-) diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/collapsible_match.rs index 6b63c2cf157ac..a42eee53459eb 100644 --- a/clippy_lints/src/collapsible_match.rs +++ b/clippy_lints/src/collapsible_match.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::visitors::LocalUsedVisitor; -use clippy_utils::{higher, is_lang_ctor, path_to_local, peel_ref_operators, SpanlessEq}; +use clippy_utils::{higher, is_lang_ctor, is_unit_expr, path_to_local, peel_ref_operators, SpanlessEq}; use if_chain::if_chain; use rustc_hir::LangItem::OptionNone; -use rustc_hir::{Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind}; +use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, MatchSource, Pat, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{MultiSpan, Span}; @@ -49,104 +49,87 @@ declare_lint_pass!(CollapsibleMatch => [COLLAPSIBLE_MATCH]); impl<'tcx> LateLintPass<'tcx> for CollapsibleMatch { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { - if let Some(higher::IfLet { - let_pat, - if_then, - if_else, - .. - }) = higher::IfLet::hir(expr) - { - check_arm(cx, if_then, None, let_pat, if_else); - - check_if_let(cx, if_then, let_pat); - } - - if let ExprKind::Match(_expr, arms, _source) = expr.kind { - if let Some(wild_arm) = arms.iter().rfind(|arm| is_wild_like(cx, &arm.pat.kind, &arm.guard)) { - for arm in arms { - check_arm(cx, arm.body, arm.guard.as_ref(), arm.pat, Some(wild_arm.body)); + match IfLetOrMatch::parse(cx, expr) { + Some(IfLetOrMatch::Match(_, arms, _)) => { + if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) { + for arm in arms { + check_arm(cx, true, arm.pat, arm.body, arm.guard.as_ref(), Some(els_arm.body)); + } } } - - if let Some(first_arm) = arms.get(0) { - check_if_let(cx, &first_arm.body, &first_arm.pat); + Some(IfLetOrMatch::IfLet(_, pat, body, els)) => { + check_arm(cx, false, pat, body, None, els); } + None => {} } } } fn check_arm<'tcx>( cx: &LateContext<'tcx>, - outer_block: &'tcx Expr<'tcx>, - outer_guard: Option<&Guard<'tcx>>, + outer_is_match: bool, outer_pat: &'tcx Pat<'tcx>, - wild_outer_block: Option<&'tcx Expr<'tcx>>, + outer_then_body: &'tcx Expr<'tcx>, + outer_guard: Option<&'tcx Guard<'tcx>>, + outer_else_body: Option<&'tcx Expr<'tcx>> ) { - let expr = strip_singleton_blocks(outer_block); + let inner_expr = strip_singleton_blocks(outer_then_body); if_chain! { - if let ExprKind::Match(expr_in, arms_inner, _) = expr.kind; - // the outer arm pattern and the inner match - if expr_in.span.ctxt() == outer_pat.span.ctxt(); - // there must be no more than two arms in the inner match for this lint - if arms_inner.len() == 2; - // no if guards on the inner match - if arms_inner.iter().all(|arm| arm.guard.is_none()); + if let Some(inner) = IfLetOrMatch::parse(cx, inner_expr); + if let Some((inner_scrutinee, inner_then_pat, inner_else_body)) = match inner { + IfLetOrMatch::IfLet(scrutinee, pat, _, els) => Some((scrutinee, pat, els)), + IfLetOrMatch::Match(scrutinee, arms, ..) => if_chain! { + // if there are more than two arms, collapsing would be non-trivial + if arms.len() == 2 && arms.iter().all(|a| a.guard.is_none()); + // one of the arms must be "wild-like" + if let Some(wild_idx) = arms.iter().rposition(|a| arm_is_wild_like(cx, a)); + then { + let (then, els) = (&arms[1 - wild_idx], &arms[wild_idx]); + Some((scrutinee, then.pat, Some(els.body))) + } else { + None + } + }, + }; + if outer_pat.span.ctxt() == inner_scrutinee.span.ctxt(); // match expression must be a local binding // match { .. } - if let Some(binding_id) = path_to_local(peel_ref_operators(cx, expr_in)); - // one of the branches must be "wild-like" - if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| is_wild_like(cx, &arm_inner.pat.kind, &arm_inner.guard)); - let (wild_inner_arm, non_wild_inner_arm) = - (&arms_inner[wild_inner_arm_idx], &arms_inner[1 - wild_inner_arm_idx]); - if !pat_contains_or(non_wild_inner_arm.pat); + if let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_scrutinee)); + if !pat_contains_or(inner_then_pat); // the binding must come from the pattern of the containing match arm // .... => match { .. } if let Some(binding_span) = find_pat_binding(outer_pat, binding_id); - // the "wild-like" branches must be equal - if wild_outer_block.map(|el| SpanlessEq::new(cx).eq_expr(wild_inner_arm.body, el)).unwrap_or(true); + // the "else" branches must be equal + if match (outer_else_body, inner_else_body) { + (None, None) => true, + (None, Some(e)) | (Some(e), None) => is_unit_expr(e), + (Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(a, b), + }; // the binding must not be used in the if guard let mut used_visitor = LocalUsedVisitor::new(cx, binding_id); - if match outer_guard { - None => true, - Some(Guard::If(expr) | Guard::IfLet(_, expr)) => !used_visitor.check_expr(expr), + if outer_guard.map_or(true, |(Guard::If(e) | Guard::IfLet(_, e))| !used_visitor.check_expr(e)); + // ...or anywhere in the inner expression + if match inner { + IfLetOrMatch::IfLet(_, _, body, els) => { + !used_visitor.check_expr(body) && els.map_or(true, |e| !used_visitor.check_expr(e)) + }, + IfLetOrMatch::Match(_, arms, ..) => !arms.iter().any(|arm| used_visitor.check_arm(arm)), }; - // ...or anywhere in the inner match - if !arms_inner.iter().any(|arm| used_visitor.check_arm(arm)); then { - span_lint_and_then( - cx, - COLLAPSIBLE_MATCH, - expr.span, - "unnecessary nested match", - |diag| { - let mut help_span = MultiSpan::from_spans(vec![binding_span, non_wild_inner_arm.pat.span]); - help_span.push_span_label(binding_span, "replace this binding".into()); - help_span.push_span_label(non_wild_inner_arm.pat.span, "with this pattern".into()); - diag.span_help(help_span, "the outer pattern can be modified to include the inner pattern"); - }, + let msg = format!( + "this `{}` can be collapsed into the outer `{}`", + if matches!(inner, IfLetOrMatch::Match(..)) { "match" } else { "if let" }, + if outer_is_match { "match" } else { "if let" }, ); - } - } -} - -fn check_if_let<'tcx>(cx: &LateContext<'tcx>, outer_expr: &'tcx Expr<'tcx>, outer_pat: &'tcx Pat<'tcx>) { - let block_inner = strip_singleton_blocks(outer_expr); - if_chain! { - if let Some(higher::IfLet { if_then: inner_if_then, let_expr: inner_let_expr, let_pat: inner_let_pat, .. }) = higher::IfLet::hir(block_inner); - if let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_let_expr)); - if let Some(binding_span) = find_pat_binding(outer_pat, binding_id); - let mut used_visitor = LocalUsedVisitor::new(cx, binding_id); - if !used_visitor.check_expr(inner_if_then); - then { span_lint_and_then( cx, COLLAPSIBLE_MATCH, - block_inner.span, - "unnecessary nested `if let` or `match`", + inner_expr.span, + &msg, |diag| { - let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_let_pat.span]); + let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_then_pat.span]); help_span.push_span_label(binding_span, "replace this binding".into()); - help_span.push_span_label(inner_let_pat.span, "with this pattern".into()); + help_span.push_span_label(inner_then_pat.span, "with this pattern".into()); diag.span_help(help_span, "the outer pattern can be modified to include the inner pattern"); }, ); @@ -168,14 +151,30 @@ fn strip_singleton_blocks<'hir>(mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> expr } -/// A "wild-like" pattern is wild ("_") or `None`. -/// For this lint to apply, both the outer and inner patterns -/// must have "wild-like" branches that can be combined. -fn is_wild_like(cx: &LateContext<'_>, pat_kind: &PatKind<'_>, arm_guard: &Option>) -> bool { - if arm_guard.is_some() { +enum IfLetOrMatch<'hir> { + Match(&'hir Expr<'hir>, &'hir [Arm<'hir>], MatchSource), + /// scrutinee, pattern, then block, else block + IfLet(&'hir Expr<'hir>, &'hir Pat<'hir>, &'hir Expr<'hir>, Option<&'hir Expr<'hir>>), +} + +impl<'hir> IfLetOrMatch<'hir> { + fn parse(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option { + match expr.kind { + ExprKind::Match(expr, arms, source) => Some(Self::Match(expr, arms, source)), + _ => higher::IfLet::hir(cx, expr).map(|higher::IfLet { let_expr, let_pat, if_then, if_else }| { + Self::IfLet(let_expr, let_pat, if_then, if_else) + }) + } + } +} + +/// A "wild-like" arm has a wild (`_`) or `None` pattern and no guard. Such arms can be "collapsed" +/// into a single wild arm without any significant loss in semantics or readability. +fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { + if arm.guard.is_some() { return false; } - match pat_kind { + match arm.pat.kind { PatKind::Binding(..) | PatKind::Wild => true, PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), _ => false, diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs index e2d3905eacb50..7dad1c31150e2 100644 --- a/clippy_lints/src/if_let_mutex.rs +++ b/clippy_lints/src/if_let_mutex.rs @@ -59,7 +59,7 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex { if_then, if_else: Some(if_else), .. - }) = higher::IfLet::hir(expr) + }) = higher::IfLet::hir(cx, expr) { op_visit.visit_expr(let_expr); if op_visit.mutex_lock_called { diff --git a/clippy_lints/src/if_let_some_result.rs b/clippy_lints/src/if_let_some_result.rs index cd813c639dbbf..fb5637fcec183 100644 --- a/clippy_lints/src/if_let_some_result.rs +++ b/clippy_lints/src/if_let_some_result.rs @@ -45,7 +45,7 @@ declare_lint_pass!(OkIfLet => [IF_LET_SOME_RESULT]); impl<'tcx> LateLintPass<'tcx> for OkIfLet { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { //begin checking variables - if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(expr); + if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(cx, expr); if let ExprKind::MethodCall(_, ok_span, ref result_types, _) = let_expr.kind; //check is expr.ok() has type Result.ok(, _) if let PatKind::TupleStruct(QPath::Resolved(_, ref x), ref 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; diff --git a/clippy_lints/src/loops/manual_flatten.rs b/clippy_lints/src/loops/manual_flatten.rs index 9f2bc3c7ebae7..5852674da5780 100644 --- a/clippy_lints/src/loops/manual_flatten.rs +++ b/clippy_lints/src/loops/manual_flatten.rs @@ -37,7 +37,7 @@ pub(super) fn check<'tcx>( if_chain! { if let Some(inner_expr) = inner_expr; - if let Some(higher::IfLet { let_pat, let_expr, if_else: None, .. }) = higher::IfLet::hir(inner_expr); + if let Some(higher::IfLet { let_pat, let_expr, if_else: None, .. }) = higher::IfLet::hir(cx, inner_expr); // Ensure match_expr in `if let` statement is the same as the pat from the for-loop if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind; if path_to_local_id(let_expr, pat_hir_id); diff --git a/clippy_lints/src/loops/while_let_loop.rs b/clippy_lints/src/loops/while_let_loop.rs index 6be410ca8e3cb..d6d3315e0a83e 100644 --- a/clippy_lints/src/loops/while_let_loop.rs +++ b/clippy_lints/src/loops/while_let_loop.rs @@ -17,7 +17,7 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &' let_expr, if_else: Some(if_else), .. - }) = higher::IfLet::hir(inner) + }) = higher::IfLet::hir(cx, inner) { if is_simple_break_expr(if_else) { could_be_while_let(cx, expr, let_pat, let_expr); diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index 53d97f775435a..161d884149075 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -49,7 +49,7 @@ impl LateLintPass<'_> for ManualMap { let_expr, if_then, if_else: Some(if_else), - }) = higher::IfLet::hir(expr) + }) = higher::IfLet::hir(cx, expr) { manage_lint(cx, expr, (&let_pat.kind, if_then), (&PatKind::Wild, if_else), let_expr); } diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index a183d0c66e8ce..3d0da472ddcbf 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -8,9 +8,9 @@ use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs}; use clippy_utils::visitors::LocalUsedVisitor; use clippy_utils::{ - get_parent_expr, in_macro, is_expn_of, is_lang_ctor, is_lint_allowed, is_refutable, is_wild, meets_msrv, msrvs, - path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks, - strip_pat_refs, + get_parent_expr, in_macro, is_expn_of, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild, + meets_msrv, msrvs, path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, + remove_blocks, strip_pat_refs, }; use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash}; use core::array; @@ -634,7 +634,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { if let ExprKind::Match(ref ex, ref arms, _) = expr.kind { check_match_ref_pats(cx, ex, arms.iter().map(|el| el.pat), expr); } - if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(expr) { + if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(cx, expr) { check_match_ref_pats(cx, let_expr, once(let_pat), expr); } } @@ -1298,7 +1298,7 @@ fn check_match_like_matches<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) let_expr, if_then, if_else: Some(if_else), - }) = higher::IfLet::hir(expr) + }) = higher::IfLet::hir(cx, expr) { return find_matches_sugg( cx, @@ -1672,14 +1672,6 @@ fn type_ranges(ranges: &[SpannedRange]) -> TypedRanges { .collect() } -fn is_unit_expr(expr: &Expr<'_>) -> bool { - match expr.kind { - ExprKind::Tup(v) if v.is_empty() => true, - ExprKind::Block(b, _) if b.stmts.is_empty() && b.expr.is_none() => true, - _ => false, - } -} - // Checks if arm has the form `None => None` fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { matches!(arm.pat.kind, PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone)) @@ -1835,7 +1827,7 @@ mod redundant_pattern_match { let_pat, let_expr, .. - }) = higher::IfLet::ast(cx, expr) + }) = higher::IfLet::hir(cx, expr) { find_sugg_for_if_let(cx, expr, let_pat, let_expr, "if", if_else.is_some()) } diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index d0b0bad5eb1cc..eff3d3abff80c 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -125,7 +125,7 @@ fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: boo fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option { if_chain! { if !in_macro(expr.span); // Don't lint macros, because it behaves weirdly - if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) }) = higher::IfLet::hir(expr); + if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) }) = higher::IfLet::hir(cx, expr); if !is_else_clause(cx.tcx, expr); if !is_result_ok(cx, let_expr); // Don't lint on Result::ok because a different lint does it already if let PatKind::TupleStruct(struct_qpath, [inner_pat], _) = &let_pat.kind; diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 91085c13ac4a4..7b6a0894e6d2f 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -97,7 +97,7 @@ impl QuestionMark { fn check_if_let_some_and_early_return_none(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) }) = higher::IfLet::hir(expr); + if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) }) = higher::IfLet::hir(cx, expr); if Self::is_option(cx, let_expr); if let PatKind::TupleStruct(ref path1, fields, None) = let_pat.kind; diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 29b698e56e3c0..957ec35be6f9b 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -79,15 +79,7 @@ pub struct IfLet<'hir> { } impl<'hir> IfLet<'hir> { - #[inline] - pub fn ast(cx: &LateContext<'tcx>, expr: &Expr<'hir>) -> Option { - let rslt = Self::hir(expr)?; - Self::is_not_within_while_context(cx, expr)?; - Some(rslt) - } - - #[inline] - pub const fn hir(expr: &Expr<'hir>) -> Option { + pub fn hir(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option { if let ExprKind::If( Expr { kind: ExprKind::Let(let_pat, let_expr, _), @@ -97,6 +89,14 @@ impl<'hir> IfLet<'hir> { if_else, ) = expr.kind { + let hir = cx.tcx.hir(); + let mut iter = hir.parent_iter(expr.hir_id); + if let Some((_, Node::Block(Block { stmts: [], .. }))) = iter.next() { + if let Some((_, Node::Expr(Expr { kind: ExprKind::Loop(_, _, LoopSource::While, _), .. }))) = iter.next() { + // while loop desugar + return None; + } + } return Some(Self { let_pat, let_expr, @@ -106,22 +106,6 @@ impl<'hir> IfLet<'hir> { } None } - - #[inline] - fn is_not_within_while_context(cx: &LateContext<'tcx>, expr: &Expr<'hir>) -> Option<()> { - let hir = cx.tcx.hir(); - let parent = hir.get_parent_node(expr.hir_id); - let parent_parent = hir.get_parent_node(parent); - let parent_parent_node = hir.get(parent_parent); - if let Node::Expr(Expr { - kind: ExprKind::Loop(_, _, LoopSource::While, _), - .. - }) = parent_parent_node - { - return None; - } - Some(()) - } } pub struct IfOrIfLet<'hir> { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 82bfce8fe789e..7d7c3b8846c3f 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -254,6 +254,10 @@ pub fn in_macro(span: Span) -> bool { } } +pub fn is_unit_expr(expr: &Expr<'_>) -> bool { + matches!(expr.kind, ExprKind::Block(Block { stmts: [], expr: None, .. }, _) | ExprKind::Tup([])) +} + /// Checks if given pattern is a wildcard (`_`) pub fn is_wild(pat: &Pat<'_>) -> bool { matches!(pat.kind, PatKind::Wild) diff --git a/tests/ui/collapsible_match.rs b/tests/ui/collapsible_match.rs index 55467cf4229de..4ce365cc7649a 100644 --- a/tests/ui/collapsible_match.rs +++ b/tests/ui/collapsible_match.rs @@ -98,6 +98,11 @@ fn lint_cases(opt_opt: Option>, res_opt: Result, String> } fn negative_cases(res_opt: Result, String>, res_res: Result, String>) { + while let Some(x) = make() { + if let Some(1) = x { + todo!(); + } + } // no wild pattern in outer match match res_opt { Ok(val) => match val { diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr index f96917f583344..c119570e8abd8 100644 --- a/tests/ui/collapsible_match.stderr +++ b/tests/ui/collapsible_match.stderr @@ -1,4 +1,4 @@ -error: unnecessary nested match +error: this `match` can be collapsed into the outer `match` --> $DIR/collapsible_match.rs:7:20 | LL | Ok(val) => match val { @@ -17,7 +17,7 @@ LL | Ok(val) => match val { LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: unnecessary nested match +error: this `match` can be collapsed into the outer `match` --> $DIR/collapsible_match.rs:16:20 | LL | Ok(val) => match val { @@ -35,7 +35,7 @@ LL | Ok(val) => match val { LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: unnecessary nested `if let` or `match` +error: this `if let` can be collapsed into the outer `if let` --> $DIR/collapsible_match.rs:25:9 | LL | / if let Some(n) = val { @@ -51,7 +51,7 @@ LL | if let Ok(val) = res_opt { LL | if let Some(n) = val { | ^^^^^^^ with this pattern -error: unnecessary nested `if let` or `match` +error: this `if let` can be collapsed into the outer `if let` --> $DIR/collapsible_match.rs:32:9 | LL | / if let Some(n) = val { @@ -69,7 +69,7 @@ LL | if let Ok(val) = res_opt { LL | if let Some(n) = val { | ^^^^^^^ with this pattern -error: unnecessary nested match +error: this `match` can be collapsed into the outer `if let` --> $DIR/collapsible_match.rs:43:9 | LL | / match val { @@ -87,7 +87,7 @@ LL | match val { LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: unnecessary nested `if let` or `match` +error: this `if let` can be collapsed into the outer `match` --> $DIR/collapsible_match.rs:52:13 | LL | / if let Some(n) = val { @@ -103,7 +103,7 @@ LL | Ok(val) => { LL | if let Some(n) = val { | ^^^^^^^ with this pattern -error: unnecessary nested match +error: this `match` can be collapsed into the outer `if let` --> $DIR/collapsible_match.rs:61:9 | LL | / match val { @@ -121,7 +121,7 @@ LL | match val { LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: unnecessary nested `if let` or `match` +error: this `if let` can be collapsed into the outer `match` --> $DIR/collapsible_match.rs:72:13 | LL | / if let Some(n) = val { @@ -139,7 +139,7 @@ LL | Ok(val) => { LL | if let Some(n) = val { | ^^^^^^^ with this pattern -error: unnecessary nested match +error: this `match` can be collapsed into the outer `match` --> $DIR/collapsible_match.rs:83:20 | LL | Ok(val) => match val { @@ -157,7 +157,7 @@ LL | Ok(val) => match val { LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: unnecessary nested match +error: this `match` can be collapsed into the outer `match` --> $DIR/collapsible_match.rs:92:22 | LL | Some(val) => match val { diff --git a/tests/ui/collapsible_match2.stderr b/tests/ui/collapsible_match2.stderr index 8975b2efbaeeb..55e70dce208a0 100644 --- a/tests/ui/collapsible_match2.stderr +++ b/tests/ui/collapsible_match2.stderr @@ -1,4 +1,4 @@ -error: unnecessary nested match +error: this `match` can be collapsed into the outer `match` --> $DIR/collapsible_match2.rs:13:34 | LL | Ok(val) if make() => match val { @@ -17,7 +17,7 @@ LL | Ok(val) if make() => match val { LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: unnecessary nested match +error: this `match` can be collapsed into the outer `match` --> $DIR/collapsible_match2.rs:20:24 | LL | Ok(val) => match val { @@ -35,7 +35,7 @@ LL | Ok(val) => match val { LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: unnecessary nested match +error: this `match` can be collapsed into the outer `match` --> $DIR/collapsible_match2.rs:34:29 | LL | $pat => match $e { @@ -57,7 +57,7 @@ LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); | replace this binding = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) -error: unnecessary nested match +error: this `match` can be collapsed into the outer `match` --> $DIR/collapsible_match2.rs:51:20 | LL | Some(s) => match *s { @@ -75,7 +75,7 @@ LL | Some(s) => match *s { LL | [n] => foo(n), | ^^^ with this pattern -error: unnecessary nested match +error: this `match` can be collapsed into the outer `match` --> $DIR/collapsible_match2.rs:60:24 | LL | Some(ref s) => match &*s { diff --git a/tests/ui/if_let_some_result.fixed b/tests/ui/if_let_some_result.fixed index 62a25ce2d128c..1bddc47721e5a 100644 --- a/tests/ui/if_let_some_result.fixed +++ b/tests/ui/if_let_some_result.fixed @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::if_let_some_result)] +#![allow(dead_code)] fn str_to_int(x: &str) -> i32 { if let Ok(y) = x.parse() { y } else { 0 } @@ -20,8 +21,8 @@ fn strange_some_no_else(x: &str) -> i32 { } } -fn main() { - let _ = str_to_int("1"); - let _ = str_to_int_ok("2"); - let _ = strange_some_no_else("3"); +fn negative() { + while let Some(1) = "".parse().ok() {} } + +fn main() {} diff --git a/tests/ui/if_let_some_result.rs b/tests/ui/if_let_some_result.rs index 234ff5e9e80e2..d4a52ec9881d7 100644 --- a/tests/ui/if_let_some_result.rs +++ b/tests/ui/if_let_some_result.rs @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::if_let_some_result)] +#![allow(dead_code)] fn str_to_int(x: &str) -> i32 { if let Some(y) = x.parse().ok() { y } else { 0 } @@ -20,8 +21,8 @@ fn strange_some_no_else(x: &str) -> i32 { } } -fn main() { - let _ = str_to_int("1"); - let _ = str_to_int_ok("2"); - let _ = strange_some_no_else("3"); +fn negative() { + while let Some(1) = "".parse().ok() {} } + +fn main() {} diff --git a/tests/ui/if_let_some_result.stderr b/tests/ui/if_let_some_result.stderr index 134ce9d24116b..bc3a5e7698d74 100644 --- a/tests/ui/if_let_some_result.stderr +++ b/tests/ui/if_let_some_result.stderr @@ -1,5 +1,5 @@ error: matching on `Some` with `ok()` is redundant - --> $DIR/if_let_some_result.rs:6:5 + --> $DIR/if_let_some_result.rs:7:5 | LL | if let Some(y) = x.parse().ok() { y } else { 0 } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -11,7 +11,7 @@ LL | if let Ok(y) = x.parse() { y } else { 0 } | ~~~~~~~~~~~~~~~~~~~~~~~~ error: matching on `Some` with `ok()` is redundant - --> $DIR/if_let_some_result.rs:16:9 + --> $DIR/if_let_some_result.rs:17:9 | LL | if let Some(y) = x . parse() . ok () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 5fec618d2e1d377a832064f7806b8180e1287ce8 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 21 Nov 2020 07:06:16 -0500 Subject: [PATCH 31/94] introduce a Coerce predicate --- clippy_utils/src/qualify_min_const_fn.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index dee9d487c78ea..8e544f580665e 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -36,6 +36,9 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&Ru ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate), ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate), ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {:#?}", predicate), + ty::PredicateKind::Coerce(_) => { + panic!("coerce predicate on function: {:#?}", predicate) + }, ty::PredicateKind::Trait(pred) => { if Some(pred.def_id()) == tcx.lang_items().sized_trait() { continue; From cd4bf7fb8e2f1c4baa7863bd87d3a1a3e1c758a3 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 21 Aug 2021 18:07:21 +0300 Subject: [PATCH 32/94] cleanup: `Span::new` -> `Span::with_lo` --- clippy_lints/src/unit_return_expecting_ord.rs | 2 +- clippy_lints/src/write.rs | 2 +- clippy_utils/src/lib.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs index ee675838c4cb3..db0f412f2a18c 100644 --- a/clippy_lints/src/unit_return_expecting_ord.rs +++ b/clippy_lints/src/unit_return_expecting_ord.rs @@ -127,7 +127,7 @@ fn check_arg<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>) -> Option<(Spa then { let data = stmt.span.data(); // Make a span out of the semicolon for the help message - Some((span, Some(Span::new(data.hi-BytePos(1), data.hi, data.ctxt)))) + Some((span, Some(data.with_lo(data.hi-BytePos(1))))) } else { Some((span, None)) } diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 4553ac704a28f..85d1f65c51f09 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -299,7 +299,7 @@ impl EarlyLintPass for Write { let nl_span = match (dest, only_nl) { // Special case of `write!(buf, "\n")`: Mark everything from the end of // `buf` for removal so no trailing comma [`writeln!(buf, )`] remains. - (Some(dest_expr), true) => Span::new(dest_expr.span.hi(), nl_span.hi(), nl_span.ctxt()), + (Some(dest_expr), true) => nl_span.with_lo(dest_expr.span.hi()), _ => nl_span, }; span_lint_and_then( diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 82bfce8fe789e..0da86a76d39eb 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -877,7 +877,7 @@ fn line_span(cx: &T, span: Span) -> Span { let source_map_and_line = cx.sess().source_map().lookup_line(span.lo()).unwrap(); let line_no = source_map_and_line.line; let line_start = source_map_and_line.sf.lines[line_no]; - Span::new(line_start, span.hi(), span.ctxt()) + span.with_lo(line_start) } /// Gets the parent node, if any. From c86071898f76d0807cc9d9ac78a7aa4aff0688ae Mon Sep 17 00:00:00 2001 From: Frank Steffahn Date: Sun, 22 Aug 2021 14:46:15 +0200 Subject: [PATCH 33/94] =?UTF-8?q?Fix=20typos=20=E2=80=9Ca=E2=80=9D?= =?UTF-8?q?=E2=86=92=E2=80=9Can=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- clippy_lints/src/loops/manual_memcpy.rs | 2 +- clippy_lints/src/non_copy_const.rs | 2 +- clippy_lints/src/ranges.rs | 2 +- clippy_lints/src/shadow.rs | 2 +- clippy_lints/src/utils/conf.rs | 2 +- clippy_utils/src/consts.rs | 2 +- clippy_utils/src/lib.rs | 2 +- clippy_utils/src/sugg.rs | 2 +- tests/ui/declare_interior_mutable_const/traits.rs | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs index 2525b14e1c5c3..2296842e86f55 100644 --- a/clippy_lints/src/loops/manual_memcpy.rs +++ b/clippy_lints/src/loops/manual_memcpy.rs @@ -268,7 +268,7 @@ impl std::ops::Sub<&MinifyingSugg<'static>> for MinifyingSugg<'static> { } } -/// a wrapper around `MinifyingSugg`, which carries a operator like currying +/// a wrapper around `MinifyingSugg`, which carries an operator like currying /// so that the suggested code become more efficient (e.g. `foo + -bar` `foo - bar`). struct Offset { value: MinifyingSugg<'static>, diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index aa3067876ebf8..6c2563358cafe 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -293,7 +293,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { }) => { if_chain! { // Lint a trait impl item only when the definition is a generic type, - // assuming a assoc const is not meant to be a interior mutable type. + // assuming an assoc const is not meant to be an interior mutable type. if let Some(of_trait_def_id) = of_trait_ref.trait_def_id(); if let Some(of_assoc_item) = specialization_graph::Node::Trait(of_trait_def_id) .item(cx.tcx, impl_item.ident, AssocKind::Const, of_trait_def_id); diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 0114a2f97a228..4fa361fedafac 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -51,7 +51,7 @@ declare_clippy_lint! { /// /// ### Known problems /// Will add unnecessary pair of parentheses when the - /// expression is not wrapped in a pair but starts with a opening parenthesis + /// expression is not wrapped in a pair but starts with an opening parenthesis /// and ends with a closing one. /// I.e., `let _ = (f()+1)..(f()+1)` results in `let _ = ((f()+1)..=f())`. /// diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index b28a37cabd40c..b9e317a3cfd03 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -74,7 +74,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does /// Checks for bindings that shadow other bindings already in - /// scope, either without a initialization or with one that does not even use + /// scope, either without an initialization or with one that does not even use /// the original value. /// /// ### Why is this bad? diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index a28b1d78f7d46..c192f9094a8a9 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -198,7 +198,7 @@ define_Conf! { (enum_variant_name_threshold: u64 = 3), /// Lint: LARGE_ENUM_VARIANT. /// - /// The maximum size of a enum's variant to avoid box suggestion + /// The maximum size of an enum's variant to avoid box suggestion (enum_variant_size_threshold: u64 = 200), /// Lint: VERBOSE_BIT_MASK. /// diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 15c27d1a996d7..9ba1381da6593 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -329,7 +329,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { vec.iter().map(|elem| self.expr(elem)).collect::>() } - /// Lookup a possibly constant expression from a `ExprKind::Path`. + /// Lookup a possibly constant expression from an `ExprKind::Path`. fn fetch_path(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>) -> Option { let res = self.typeck_results.qpath_res(qpath, id); match res { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 82bfce8fe789e..2a13b5ee3474f 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -583,7 +583,7 @@ pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio /// For example, if `e` represents the `v[0].a.b[x]` /// this method will return a tuple, composed of a `Vec` /// containing the `Expr`s for `v[0], v[0].a, v[0].a.b, v[0].a.b[x]` -/// and a `Expr` for root of them, `v` +/// and an `Expr` for root of them, `v` fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) { let mut result = vec![]; let root = loop { diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 3b494e1fc8535..65d93e8f86e43 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -434,7 +434,7 @@ pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> matches!(op, AssocOp::ShiftLeft | AssocOp::ShiftRight) } - /// Returns `true` if the operator is a arithmetic operator + /// Returns `true` if the operator is an arithmetic operator /// (i.e., `+`, `-`, `*`, `/`, `%`). fn is_arith(op: AssocOp) -> bool { matches!( diff --git a/tests/ui/declare_interior_mutable_const/traits.rs b/tests/ui/declare_interior_mutable_const/traits.rs index 535147ccc6451..256a336db821d 100644 --- a/tests/ui/declare_interior_mutable_const/traits.rs +++ b/tests/ui/declare_interior_mutable_const/traits.rs @@ -117,7 +117,7 @@ impl SelfType for AtomicUsize { const WRAPPED_SELF: Option = Some(AtomicUsize::new(21)); //~ ERROR interior mutable } -// Even though a constant contains a generic type, if it also have a interior mutable type, +// Even though a constant contains a generic type, if it also have an interior mutable type, // it should be linted at the definition site. trait BothOfCellAndGeneric { // this is a false negative in the current implementation. From 21da42ce29c33e4a33b215a93b19bf8b5f06228d Mon Sep 17 00:00:00 2001 From: Frank Steffahn Date: Sun, 22 Aug 2021 17:27:18 +0200 Subject: [PATCH 34/94] =?UTF-8?q?Fix=20more=20=E2=80=9Ca=E2=80=9D/?= =?UTF-8?q?=E2=80=9Can=E2=80=9D=20typos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- clippy_lints/src/formatting.rs | 2 +- clippy_lints/src/non_copy_const.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/formatting.rs b/clippy_lints/src/formatting.rs index b4cf1971d78dc..4dd0ffe77ea49 100644 --- a/clippy_lints/src/formatting.rs +++ b/clippy_lints/src/formatting.rs @@ -164,7 +164,7 @@ fn check_unop(cx: &EarlyContext<'_>, expr: &Expr) { if !differing_macro_contexts(lhs.span, rhs.span) && !lhs.span.from_expansion(); // span between BinOp LHS and RHS let binop_span = lhs.span.between(rhs.span); - // if RHS is a UnOp + // if RHS is an UnOp if let ExprKind::Unary(op, ref un_rhs) = rhs.kind; // from UnOp operator to UnOp operand let unop_operand_span = rhs.span.until(un_rhs.span); diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 6c2563358cafe..3f9110295fc6b 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -122,7 +122,7 @@ fn is_unfrozen<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { // Ignore types whose layout is unknown since `is_freeze` reports every generic types as `!Freeze`, // making it indistinguishable from `UnsafeCell`. i.e. it isn't a tool to prove a type is // 'unfrozen'. However, this code causes a false negative in which - // a type contains a layout-unknown type, but also a unsafe cell like `const CELL: Cell`. + // a type contains a layout-unknown type, but also an unsafe cell like `const CELL: Cell`. // Yet, it's better than `ty.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_PROJECTION)` // since it works when a pointer indirection involves (`Cell<*const T>`). // Making up a `ParamEnv` where every generic params and assoc types are `Freeze`is another option; @@ -266,7 +266,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { // in other words, lint consts whose value *could* be unfrozen, not definitely is. // This feels inconsistent with how the lint treats generic types, // which avoids linting types which potentially become unfrozen. - // One could check whether a unfrozen type have a *frozen variant* + // One could check whether an unfrozen type have a *frozen variant* // (like `body_id_opt.map_or_else(|| !has_frozen_variant(...), ...)`), // and do the same as the case of generic types at impl items. // Note that it isn't sufficient to check if it has an enum From 8b6529e0486dbd0c49217d5f39018d520d64aff1 Mon Sep 17 00:00:00 2001 From: Frank Steffahn Date: Sun, 22 Aug 2021 18:15:49 +0200 Subject: [PATCH 35/94] =?UTF-8?q?Fix=20typos=20=E2=80=9Can=E2=80=9D?= =?UTF-8?q?=E2=86=92=E2=80=9Ca=E2=80=9D=20and=20a=20few=20different=20ones?= =?UTF-8?q?=20that=20appeared=20in=20the=20same=20search?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- clippy_lints/src/ptr_eq.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/ptr_eq.rs b/clippy_lints/src/ptr_eq.rs index d6d7049fb61b2..3258c9fb3fed0 100644 --- a/clippy_lints/src/ptr_eq.rs +++ b/clippy_lints/src/ptr_eq.rs @@ -74,7 +74,7 @@ impl LateLintPass<'_> for PtrEq { } } -// If the given expression is a cast to an usize, return the lhs of the cast +// If the given expression is a cast to a usize, return the lhs of the cast // E.g., `foo as *const _ as usize` returns `foo as *const _`. fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { if cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize { From 0a021d5900a9ee55875fb247614a0671b71d3271 Mon Sep 17 00:00:00 2001 From: Labelray Date: Fri, 6 Aug 2021 22:46:20 +0800 Subject: [PATCH 36/94] Add new lints `negative_feature_names` and `redundant_feature_names` --- CHANGELOG.md | 2 + clippy_lints/src/feature_name.rs | 164 ++++++++++++++++++ clippy_lints/src/lib.rs | 6 + tests/ui-cargo/feature_name/fail/Cargo.toml | 21 +++ tests/ui-cargo/feature_name/fail/src/main.rs | 7 + .../feature_name/fail/src/main.stderr | 44 +++++ tests/ui-cargo/feature_name/pass/Cargo.toml | 9 + tests/ui-cargo/feature_name/pass/src/main.rs | 7 + tests/ui/missing-doc-crate.stderr | 0 .../missing_const_for_fn/cant_be_const.stderr | 0 tests/ui/rc_buffer_redefined_string.stderr | 0 11 files changed, 260 insertions(+) create mode 100644 clippy_lints/src/feature_name.rs create mode 100644 tests/ui-cargo/feature_name/fail/Cargo.toml create mode 100644 tests/ui-cargo/feature_name/fail/src/main.rs create mode 100644 tests/ui-cargo/feature_name/fail/src/main.stderr create mode 100644 tests/ui-cargo/feature_name/pass/Cargo.toml create mode 100644 tests/ui-cargo/feature_name/pass/src/main.rs delete mode 100644 tests/ui/missing-doc-crate.stderr delete mode 100644 tests/ui/missing_const_for_fn/cant_be_const.stderr delete mode 100644 tests/ui/rc_buffer_redefined_string.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index ac745793dda53..1c182c05f509d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2828,6 +2828,7 @@ Released 2018-09-13 [`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update [`neg_cmp_op_on_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_cmp_op_on_partial_ord [`neg_multiply`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_multiply +[`negative_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#negative_feature_names [`never_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#never_loop [`new_ret_no_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_ret_no_self [`new_without_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default @@ -2881,6 +2882,7 @@ Released 2018-09-13 [`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call [`redundant_closure_for_method_calls`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls [`redundant_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_else +[`redundant_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_feature_names [`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names [`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern [`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching diff --git a/clippy_lints/src/feature_name.rs b/clippy_lints/src/feature_name.rs new file mode 100644 index 0000000000000..eef1407a80cf1 --- /dev/null +++ b/clippy_lints/src/feature_name.rs @@ -0,0 +1,164 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::{diagnostics::span_lint, is_lint_allowed}; +use rustc_hir::{Crate, CRATE_HIR_ID}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::DUMMY_SP; + +declare_clippy_lint! { + /// ### What it does + /// Checks for feature names with prefix `use-`, `with-` or suffix `-support` + /// + /// ### Why is this bad? + /// These prefixes and suffixes have no significant meaning. + /// + /// ### Example + /// ```toml + /// # The `Cargo.toml` with feature name redundancy + /// [features] + /// default = ["use-abc", "with-def", "ghi-support"] + /// use-abc = [] // redundant + /// with-def = [] // redundant + /// ghi-support = [] // redundant + /// ``` + /// + /// Use instead: + /// ```toml + /// [features] + /// default = ["abc", "def", "ghi"] + /// abc = [] + /// def = [] + /// ghi = [] + /// ``` + /// + pub REDUNDANT_FEATURE_NAMES, + cargo, + "usage of a redundant feature name" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for negative feature names with prefix `no-` or `not-` + /// + /// ### Why is this bad? + /// Features are supposed to be additive, and negatively-named features violate it. + /// + /// ### Example + /// ```toml + /// # The `Cargo.toml` with negative feature names + /// [features] + /// default = [] + /// no-abc = [] + /// not-def = [] + /// + /// ``` + /// Use instead: + /// ```toml + /// [features] + /// default = ["abc", "def"] + /// abc = [] + /// def = [] + /// + /// ``` + pub NEGATIVE_FEATURE_NAMES, + cargo, + "usage of a negative feature name" +} + +declare_lint_pass!(FeatureName => [REDUNDANT_FEATURE_NAMES, NEGATIVE_FEATURE_NAMES]); + +static PREFIXES: [&str; 8] = ["no-", "no_", "not-", "not_", "use-", "use_", "with-", "with_"]; +static SUFFIXES: [&str; 2] = ["-support", "_support"]; + +fn is_negative_prefix(s: &str) -> bool { + s.starts_with("no") +} + +fn lint(cx: &LateContext<'_>, feature: &str, substring: &str, is_prefix: bool) { + let is_negative = is_prefix && is_negative_prefix(substring); + span_lint_and_help( + cx, + if is_negative { + NEGATIVE_FEATURE_NAMES + } else { + REDUNDANT_FEATURE_NAMES + }, + DUMMY_SP, + &format!( + "the \"{}\" {} in the feature name \"{}\" is {}", + substring, + if is_prefix { "prefix" } else { "suffix" }, + feature, + if is_negative { "negative" } else { "redundant" } + ), + None, + &format!( + "consider renaming the feature to \"{}\"{}", + if is_prefix { + feature.strip_prefix(substring) + } else { + feature.strip_suffix(substring) + } + .unwrap(), + if is_negative { + ", but make sure the feature adds functionality" + } else { + "" + } + ), + ); +} + +impl LateLintPass<'_> for FeatureName { + fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) { + if is_lint_allowed(cx, REDUNDANT_FEATURE_NAMES, CRATE_HIR_ID) + && is_lint_allowed(cx, NEGATIVE_FEATURE_NAMES, CRATE_HIR_ID) + { + return; + } + + let metadata = unwrap_cargo_metadata!(cx, REDUNDANT_FEATURE_NAMES, false); + + for package in metadata.packages { + let mut features: Vec<&String> = package.features.keys().collect(); + features.sort(); + for feature in features { + let prefix_opt = { + let i = PREFIXES.partition_point(|prefix| prefix < &feature.as_str()); + if i > 0 && feature.starts_with(PREFIXES[i - 1]) { + Some(PREFIXES[i - 1]) + } else { + None + } + }; + if let Some(prefix) = prefix_opt { + lint(cx, feature, prefix, true); + } + + let suffix_opt: Option<&str> = { + let i = SUFFIXES.partition_point(|suffix| { + suffix.bytes().rev().cmp(feature.bytes().rev()) == std::cmp::Ordering::Less + }); + if i > 0 && feature.ends_with(SUFFIXES[i - 1]) { + Some(SUFFIXES[i - 1]) + } else { + None + } + }; + if let Some(suffix) = suffix_opt { + lint(cx, feature, suffix, false); + } + } + } + } +} + +#[test] +fn test_prefixes_sorted() { + let mut sorted_prefixes = PREFIXES; + sorted_prefixes.sort_unstable(); + assert_eq!(PREFIXES, sorted_prefixes); + let mut sorted_suffixes = SUFFIXES; + sorted_suffixes.sort_by(|a, b| a.bytes().rev().cmp(b.bytes().rev())); + assert_eq!(SUFFIXES, sorted_suffixes); +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f49b382c5ea3b..ce671be069af4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -213,6 +213,7 @@ mod exhaustive_items; mod exit; mod explicit_write; mod fallible_impl_from; +mod feature_name; mod float_equality_without_abs; mod float_literal; mod floating_point_arithmetic; @@ -628,6 +629,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: exit::EXIT, explicit_write::EXPLICIT_WRITE, fallible_impl_from::FALLIBLE_IMPL_FROM, + feature_name::NEGATIVE_FEATURE_NAMES, + feature_name::REDUNDANT_FEATURE_NAMES, float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS, float_literal::EXCESSIVE_PRECISION, float_literal::LOSSY_FLOAT_LITERAL, @@ -1781,6 +1784,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::cargo", Some("clippy_cargo"), vec![ LintId::of(cargo_common_metadata::CARGO_COMMON_METADATA), + LintId::of(feature_name::NEGATIVE_FEATURE_NAMES), + LintId::of(feature_name::REDUNDANT_FEATURE_NAMES), LintId::of(multiple_crate_versions::MULTIPLE_CRATE_VERSIONS), LintId::of(wildcard_dependencies::WILDCARD_DEPENDENCIES), ]); @@ -2105,6 +2110,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(move || box disallowed_script_idents::DisallowedScriptIdents::new(&scripts)); store.register_late_pass(|| box strlen_on_c_strings::StrlenOnCStrings); store.register_late_pass(move || box self_named_constructors::SelfNamedConstructors); + store.register_late_pass(move || box feature_name::FeatureName); } #[rustfmt::skip] diff --git a/tests/ui-cargo/feature_name/fail/Cargo.toml b/tests/ui-cargo/feature_name/fail/Cargo.toml new file mode 100644 index 0000000000000..97d51462a946f --- /dev/null +++ b/tests/ui-cargo/feature_name/fail/Cargo.toml @@ -0,0 +1,21 @@ + +# Content that triggers the lint goes here + +[package] +name = "feature_name" +version = "0.1.0" +publish = false + +[workspace] + +[features] +use-qwq = [] +use_qwq = [] +with-owo = [] +with_owo = [] +qvq-support = [] +qvq_support = [] +no-qaq = [] +no_qaq = [] +not-orz = [] +not_orz = [] diff --git a/tests/ui-cargo/feature_name/fail/src/main.rs b/tests/ui-cargo/feature_name/fail/src/main.rs new file mode 100644 index 0000000000000..64f01a98c90e9 --- /dev/null +++ b/tests/ui-cargo/feature_name/fail/src/main.rs @@ -0,0 +1,7 @@ +// compile-flags: --crate-name=feature_name +#![warn(clippy::redundant_feature_names)] +#![warn(clippy::negative_feature_names)] + +fn main() { + // test code goes here +} diff --git a/tests/ui-cargo/feature_name/fail/src/main.stderr b/tests/ui-cargo/feature_name/fail/src/main.stderr new file mode 100644 index 0000000000000..b9e6cb49bc982 --- /dev/null +++ b/tests/ui-cargo/feature_name/fail/src/main.stderr @@ -0,0 +1,44 @@ +error: the "no-" prefix in the feature name "no-qaq" is negative + | + = note: `-D clippy::negative-feature-names` implied by `-D warnings` + = help: consider renaming the feature to "qaq", but make sure the feature adds functionality + +error: the "no_" prefix in the feature name "no_qaq" is negative + | + = help: consider renaming the feature to "qaq", but make sure the feature adds functionality + +error: the "not-" prefix in the feature name "not-orz" is negative + | + = help: consider renaming the feature to "orz", but make sure the feature adds functionality + +error: the "not_" prefix in the feature name "not_orz" is negative + | + = help: consider renaming the feature to "orz", but make sure the feature adds functionality + +error: the "-support" suffix in the feature name "qvq-support" is redundant + | + = note: `-D clippy::redundant-feature-names` implied by `-D warnings` + = help: consider renaming the feature to "qvq" + +error: the "_support" suffix in the feature name "qvq_support" is redundant + | + = help: consider renaming the feature to "qvq" + +error: the "use-" prefix in the feature name "use-qwq" is redundant + | + = help: consider renaming the feature to "qwq" + +error: the "use_" prefix in the feature name "use_qwq" is redundant + | + = help: consider renaming the feature to "qwq" + +error: the "with-" prefix in the feature name "with-owo" is redundant + | + = help: consider renaming the feature to "owo" + +error: the "with_" prefix in the feature name "with_owo" is redundant + | + = help: consider renaming the feature to "owo" + +error: aborting due to 10 previous errors + diff --git a/tests/ui-cargo/feature_name/pass/Cargo.toml b/tests/ui-cargo/feature_name/pass/Cargo.toml new file mode 100644 index 0000000000000..cf947312bd479 --- /dev/null +++ b/tests/ui-cargo/feature_name/pass/Cargo.toml @@ -0,0 +1,9 @@ + +# This file should not trigger the lint + +[package] +name = "feature_name" +version = "0.1.0" +publish = false + +[workspace] diff --git a/tests/ui-cargo/feature_name/pass/src/main.rs b/tests/ui-cargo/feature_name/pass/src/main.rs new file mode 100644 index 0000000000000..64f01a98c90e9 --- /dev/null +++ b/tests/ui-cargo/feature_name/pass/src/main.rs @@ -0,0 +1,7 @@ +// compile-flags: --crate-name=feature_name +#![warn(clippy::redundant_feature_names)] +#![warn(clippy::negative_feature_names)] + +fn main() { + // test code goes here +} diff --git a/tests/ui/missing-doc-crate.stderr b/tests/ui/missing-doc-crate.stderr deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/tests/ui/missing_const_for_fn/cant_be_const.stderr b/tests/ui/missing_const_for_fn/cant_be_const.stderr deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/tests/ui/rc_buffer_redefined_string.stderr b/tests/ui/rc_buffer_redefined_string.stderr deleted file mode 100644 index e69de29bb2d1d..0000000000000 From 68b4a43e23d9c7e847e71cdbfa9d915df4997e38 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 23 Aug 2021 10:58:56 -0500 Subject: [PATCH 37/94] Remove stderr limit --- .github/workflows/clippy_dev.yml | 3 -- clippy_dev/src/lib.rs | 1 - clippy_dev/src/main.rs | 9 +---- clippy_dev/src/stderr_length_check.rs | 51 --------------------------- 4 files changed, 1 insertion(+), 63 deletions(-) delete mode 100644 clippy_dev/src/stderr_length_check.rs diff --git a/.github/workflows/clippy_dev.yml b/.github/workflows/clippy_dev.yml index 95da775b7bc36..9a5416153abdb 100644 --- a/.github/workflows/clippy_dev.yml +++ b/.github/workflows/clippy_dev.yml @@ -42,9 +42,6 @@ jobs: run: cargo build --features deny-warnings working-directory: clippy_dev - - name: Test limit_stderr_length - run: cargo dev limit_stderr_length - - name: Test update_lints run: cargo dev update_lints --check diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 72bdaf8d59282..e05db7af58677 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -17,7 +17,6 @@ pub mod fmt; pub mod new_lint; pub mod serve; pub mod setup; -pub mod stderr_length_check; pub mod update_lints; static DEC_CLIPPY_LINT_RE: SyncLazy = SyncLazy::new(|| { diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index ff324ff6ee6ff..8fdeba9842af3 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -3,7 +3,7 @@ #![warn(rust_2018_idioms, unused_lifetimes)] use clap::{App, AppSettings, Arg, ArgMatches, SubCommand}; -use clippy_dev::{bless, fmt, new_lint, serve, setup, stderr_length_check, update_lints}; +use clippy_dev::{bless, fmt, new_lint, serve, setup, update_lints}; fn main() { let matches = get_clap_config(); @@ -33,9 +33,6 @@ fn main() { Err(e) => eprintln!("Unable to create lint: {}", e), } }, - ("limit_stderr_length", _) => { - stderr_length_check::check(); - }, ("setup", Some(sub_command)) => match sub_command.subcommand() { ("intellij", Some(matches)) => setup::intellij::setup_rustc_src( matches @@ -152,10 +149,6 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { .takes_value(true), ), ) - .subcommand( - SubCommand::with_name("limit_stderr_length") - .about("Ensures that stderr files do not grow longer than a certain amount of lines."), - ) .subcommand( SubCommand::with_name("setup") .about("Support for setting up your personal development environment") diff --git a/clippy_dev/src/stderr_length_check.rs b/clippy_dev/src/stderr_length_check.rs deleted file mode 100644 index e02b6f7da5f7b..0000000000000 --- a/clippy_dev/src/stderr_length_check.rs +++ /dev/null @@ -1,51 +0,0 @@ -use crate::clippy_project_root; -use std::ffi::OsStr; -use std::fs; -use std::path::{Path, PathBuf}; -use walkdir::WalkDir; - -// The maximum length allowed for stderr files. -// -// We limit this because small files are easier to deal with than bigger files. -const LENGTH_LIMIT: usize = 200; - -pub fn check() { - let exceeding_files: Vec<_> = exceeding_stderr_files(); - - if !exceeding_files.is_empty() { - eprintln!("Error: stderr files exceeding limit of {} lines:", LENGTH_LIMIT); - for (path, count) in exceeding_files { - println!("{}: {}", path.display(), count); - } - std::process::exit(1); - } -} - -fn exceeding_stderr_files() -> Vec<(PathBuf, usize)> { - // We use `WalkDir` instead of `fs::read_dir` here in order to recurse into subdirectories. - WalkDir::new(clippy_project_root().join("tests/ui")) - .into_iter() - .filter_map(Result::ok) - .filter(|f| !f.file_type().is_dir()) - .filter_map(|e| { - let p = e.into_path(); - let count = count_linenumbers(&p); - if p.extension() == Some(OsStr::new("stderr")) && count > LENGTH_LIMIT { - Some((p, count)) - } else { - None - } - }) - .collect() -} - -#[must_use] -fn count_linenumbers(filepath: &Path) -> usize { - match fs::read(filepath) { - Ok(content) => bytecount::count(&content, b'\n'), - Err(e) => { - eprintln!("Failed to read file: {}", e); - 0 - }, - } -} From 2f73776525cfe7a47e4550ce640b5565ddd9315c Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 23 Aug 2021 13:39:36 -0500 Subject: [PATCH 38/94] Move branches_sharing_code to nursery --- clippy_lints/src/copies.rs | 2 +- clippy_lints/src/lib.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 2dcd55457993c..29f3c911a306a 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -148,7 +148,7 @@ declare_clippy_lint! { /// }; /// ``` pub BRANCHES_SHARING_CODE, - complexity, + nursery, "`if` statement with shared code in all blocks" } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c98803adbed36..30c684dc7444c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1200,7 +1200,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(collapsible_if::COLLAPSIBLE_IF), LintId::of(collapsible_match::COLLAPSIBLE_MATCH), LintId::of(comparison_chain::COMPARISON_CHAIN), - LintId::of(copies::BRANCHES_SHARING_CODE), LintId::of(copies::IFS_SAME_COND), LintId::of(copies::IF_SAME_THEN_ELSE), LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), @@ -1589,7 +1588,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(booleans::NONMINIMAL_BOOL), LintId::of(casts::CHAR_LIT_AS_U8), LintId::of(casts::UNNECESSARY_CAST), - LintId::of(copies::BRANCHES_SHARING_CODE), LintId::of(double_comparison::DOUBLE_COMPARISONS), LintId::of(double_parens::DOUBLE_PARENS), LintId::of(duration_subsec::DURATION_SUBSEC), @@ -1798,6 +1796,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR), LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY), + LintId::of(copies::BRANCHES_SHARING_CODE), LintId::of(disallowed_method::DISALLOWED_METHOD), LintId::of(disallowed_type::DISALLOWED_TYPE), LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM), From 641be558fcdbddbabc1b6af841c05b61dbdd9d5f Mon Sep 17 00:00:00 2001 From: lengyijun Date: Sun, 22 Aug 2021 15:22:28 +0800 Subject: [PATCH 39/94] redundant_allocation: fix 7487 --- .../src/types/redundant_allocation.rs | 8 +++++++- tests/ui/redundant_allocation.rs | 20 +++++++++++++++++++ tests/ui/redundant_allocation.stderr | 11 +++++++++- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs index 8e83dcbf704e0..ac7bdd6a1ebeb 100644 --- a/clippy_lints/src/types/redundant_allocation.rs +++ b/clippy_lints/src/types/redundant_allocation.rs @@ -54,7 +54,13 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ _ => return false, }; let inner_span = match get_qpath_generic_tys(inner_qpath).next() { - Some(ty) => ty.span, + Some(ty) => { + // Box> is smaller than Box because of wide pointers + if matches!(ty.kind, TyKind::TraitObject(..)) { + return false; + } + ty.span + }, None => return false, }; if inner_sym == outer_sym { diff --git a/tests/ui/redundant_allocation.rs b/tests/ui/redundant_allocation.rs index 1b4f2a66c705e..52fbc91e32555 100644 --- a/tests/ui/redundant_allocation.rs +++ b/tests/ui/redundant_allocation.rs @@ -77,4 +77,24 @@ mod outer_arc { } } +// https://github.com/rust-lang/rust-clippy/issues/7487 +mod box_dyn { + use std::boxed::Box; + use std::rc::Rc; + use std::sync::Arc; + + pub trait T {} + + struct S { + a: Box>, + b: Rc>, + c: Arc>, + } + + pub fn test_box(_: Box>) {} + pub fn test_rc(_: Rc>) {} + pub fn test_arc(_: Arc>) {} + pub fn test_rc_box(_: Rc>>) {} +} + fn main() {} diff --git a/tests/ui/redundant_allocation.stderr b/tests/ui/redundant_allocation.stderr index fdab74eb538e3..c3b10e5f5e679 100644 --- a/tests/ui/redundant_allocation.stderr +++ b/tests/ui/redundant_allocation.stderr @@ -134,5 +134,14 @@ LL | pub fn arc_test9(foo: Arc>) -> Arc>> { = note: `Rc>` is already on the heap, `Arc>>` makes an extra allocation = help: consider using just `Arc>` or `Rc>` -error: aborting due to 15 previous errors +error: usage of `Rc>>` + --> $DIR/redundant_allocation.rs:97:27 + | +LL | pub fn test_rc_box(_: Rc>>) {} + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `Box>` is already on the heap, `Rc>>` makes an extra allocation + = help: consider using just `Rc>` or `Box>` + +error: aborting due to 16 previous errors From 31738b1da3e4033ff7898ffc869e96d7faba1775 Mon Sep 17 00:00:00 2001 From: Devin Ragotzy Date: Fri, 30 Jul 2021 17:28:44 -0400 Subject: [PATCH 40/94] Add module_style lint to restriction Add tests for disallowed_mod in ui-cargo test section Use correct algorithm to determine if mod.rs is missing Move to two lints and remove config option Switch lint names so they read "warn on ..." Emit the same help info for self_named_mod_file warnings Bail when both lints are Allow Reword help message for both module_style lints --- CHANGELOG.md | 2 + clippy_lints/src/lib.rs | 6 + clippy_lints/src/module_style.rs | 178 ++++++++++++++++++ .../ui-cargo/module_style/fail_mod/Cargo.toml | 8 + .../module_style/fail_mod/src/bad/inner.rs | 1 + .../fail_mod/src/bad/inner/stuff.rs | 3 + .../fail_mod/src/bad/inner/stuff/most.rs | 1 + .../module_style/fail_mod/src/bad/mod.rs | 3 + .../module_style/fail_mod/src/main.rs | 9 + .../module_style/fail_mod/src/main.stderr | 19 ++ .../module_style/fail_no_mod/Cargo.toml | 8 + .../module_style/fail_no_mod/src/bad/mod.rs | 1 + .../module_style/fail_no_mod/src/main.rs | 7 + .../module_style/fail_no_mod/src/main.stderr | 11 ++ .../ui-cargo/module_style/pass_mod/Cargo.toml | 8 + .../module_style/pass_mod/src/bad/mod.rs | 1 + .../module_style/pass_mod/src/main.rs | 10 + .../module_style/pass_mod/src/more/foo.rs | 1 + .../pass_mod/src/more/inner/mod.rs | 1 + .../module_style/pass_mod/src/more/mod.rs | 2 + .../module_style/pass_no_mod/Cargo.toml | 8 + .../module_style/pass_no_mod/src/good.rs | 1 + .../module_style/pass_no_mod/src/main.rs | 7 + 23 files changed, 296 insertions(+) create mode 100644 clippy_lints/src/module_style.rs create mode 100644 tests/ui-cargo/module_style/fail_mod/Cargo.toml create mode 100644 tests/ui-cargo/module_style/fail_mod/src/bad/inner.rs create mode 100644 tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff.rs create mode 100644 tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff/most.rs create mode 100644 tests/ui-cargo/module_style/fail_mod/src/bad/mod.rs create mode 100644 tests/ui-cargo/module_style/fail_mod/src/main.rs create mode 100644 tests/ui-cargo/module_style/fail_mod/src/main.stderr create mode 100644 tests/ui-cargo/module_style/fail_no_mod/Cargo.toml create mode 100644 tests/ui-cargo/module_style/fail_no_mod/src/bad/mod.rs create mode 100644 tests/ui-cargo/module_style/fail_no_mod/src/main.rs create mode 100644 tests/ui-cargo/module_style/fail_no_mod/src/main.stderr create mode 100644 tests/ui-cargo/module_style/pass_mod/Cargo.toml create mode 100644 tests/ui-cargo/module_style/pass_mod/src/bad/mod.rs create mode 100644 tests/ui-cargo/module_style/pass_mod/src/main.rs create mode 100644 tests/ui-cargo/module_style/pass_mod/src/more/foo.rs create mode 100644 tests/ui-cargo/module_style/pass_mod/src/more/inner/mod.rs create mode 100644 tests/ui-cargo/module_style/pass_mod/src/more/mod.rs create mode 100644 tests/ui-cargo/module_style/pass_no_mod/Cargo.toml create mode 100644 tests/ui-cargo/module_style/pass_no_mod/src/good.rs create mode 100644 tests/ui-cargo/module_style/pass_no_mod/src/main.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index b9e67d361c0c3..4bbc123390212 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2796,6 +2796,7 @@ Released 2018-09-13 [`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc [`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes [`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals +[`mod_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#mod_module_files [`module_inception`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_inception [`module_name_repetitions`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions [`modulo_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_arithmetic @@ -2904,6 +2905,7 @@ Released 2018-09-13 [`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some [`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment [`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors +[`self_named_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_module_files [`semicolon_if_nothing_returned`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned [`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse [`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1fadaf4770a81..8217870117610 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -274,6 +274,7 @@ mod missing_const_for_fn; mod missing_doc; mod missing_enforced_import_rename; mod missing_inline; +mod module_style; mod modulo_arithmetic; mod multiple_crate_versions; mod mut_key; @@ -826,6 +827,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS, missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES, missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS, + module_style::MOD_MODULE_FILES, + module_style::SELF_NAMED_MODULE_FILES, modulo_arithmetic::MODULO_ARITHMETIC, multiple_crate_versions::MULTIPLE_CRATE_VERSIONS, mut_key::MUTABLE_KEY_TYPE, @@ -1035,6 +1038,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS), LintId::of(missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES), LintId::of(missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS), + LintId::of(module_style::MOD_MODULE_FILES), + LintId::of(module_style::SELF_NAMED_MODULE_FILES), LintId::of(modulo_arithmetic::MODULO_ARITHMETIC), LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN), LintId::of(panic_unimplemented::PANIC), @@ -2107,6 +2112,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box manual_map::ManualMap); store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv)); store.register_early_pass(|| box bool_assert_comparison::BoolAssertComparison); + store.register_early_pass(move || box module_style::ModStyle); store.register_late_pass(|| box unused_async::UnusedAsync); let disallowed_types = conf.disallowed_types.iter().cloned().collect::>(); store.register_late_pass(move || box disallowed_type::DisallowedType::new(&disallowed_types)); diff --git a/clippy_lints/src/module_style.rs b/clippy_lints/src/module_style.rs new file mode 100644 index 0000000000000..73e93cf5a3ec5 --- /dev/null +++ b/clippy_lints/src/module_style.rs @@ -0,0 +1,178 @@ +use std::{ + ffi::OsString, + path::{Component, Path}, +}; + +use rustc_ast::ast; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{FileName, RealFileName, SourceFile, Span, SyntaxContext}; + +declare_clippy_lint! { + /// ### What it does + /// Checks that module layout uses only self named module files, bans mod.rs files. + /// + /// ### Why is this bad? + /// Having multiple module layout styles in a project can be confusing. + /// + /// ### Example + /// ```text + /// src/ + /// stuff/ + /// stuff_files.rs + /// mod.rs + /// lib.rs + /// ``` + /// Use instead: + /// ```text + /// src/ + /// stuff/ + /// stuff_files.rs + /// stuff.rs + /// lib.rs + /// ``` + pub MOD_MODULE_FILES, + restriction, + "checks that module layout is consistent" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks that module layout uses only mod.rs files. + /// + /// ### Why is this bad? + /// Having multiple module layout styles in a project can be confusing. + /// + /// ### Example + /// ```text + /// src/ + /// stuff/ + /// stuff_files.rs + /// stuff.rs + /// lib.rs + /// ``` + /// Use instead: + /// ```text + /// src/ + /// stuff/ + /// stuff_files.rs + /// mod.rs + /// lib.rs + /// ``` + + pub SELF_NAMED_MODULE_FILES, + restriction, + "checks that module layout is consistent" +} + +pub struct ModStyle; + +impl_lint_pass!(ModStyle => [MOD_MODULE_FILES, SELF_NAMED_MODULE_FILES]); + +impl EarlyLintPass for ModStyle { + fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) { + if cx.builder.lint_level(MOD_MODULE_FILES).0 == Level::Allow + && cx.builder.lint_level(SELF_NAMED_MODULE_FILES).0 == Level::Allow + { + return; + } + + let files = cx.sess.source_map().files(); + + let trim_to_src = if let RealFileName::LocalPath(p) = &cx.sess.working_dir { + p.to_string_lossy() + } else { + return; + }; + + // `folder_segments` is all unique folder path segments `path/to/foo.rs` gives + // `[path, to]` but not foo + let mut folder_segments = FxHashSet::default(); + // `mod_folders` is all the unique folder names that contain a mod.rs file + let mut mod_folders = FxHashSet::default(); + // `file_map` maps file names to the full path including the file name + // `{ foo => path/to/foo.rs, .. } + let mut file_map = FxHashMap::default(); + for file in files.iter() { + match &file.name { + FileName::Real(RealFileName::LocalPath(lp)) + if lp.to_string_lossy().starts_with(trim_to_src.as_ref()) => + { + let p = lp.to_string_lossy(); + let path = Path::new(p.trim_start_matches(trim_to_src.as_ref())); + if let Some(stem) = path.file_stem() { + file_map.insert(stem.to_os_string(), (file, path.to_owned())); + } + process_paths_for_mod_files(path, &mut folder_segments, &mut mod_folders); + check_self_named_mod_exists(cx, path, file); + } + _ => {}, + } + } + + for folder in &folder_segments { + if !mod_folders.contains(folder) { + if let Some((file, path)) = file_map.get(folder) { + let mut correct = path.clone(); + correct.pop(); + correct.push(folder); + correct.push("mod.rs"); + cx.struct_span_lint( + SELF_NAMED_MODULE_FILES, + Span::new(file.start_pos, file.start_pos, SyntaxContext::root()), + |build| { + let mut lint = + build.build(&format!("`mod.rs` files are required, found `{}`", path.display())); + lint.help(&format!("move `{}` to `{}`", path.display(), correct.display(),)); + lint.emit(); + }, + ); + } + } + } + } +} + +/// For each `path` we add each folder component to `folder_segments` and if the file name +/// is `mod.rs` we add it's parent folder to `mod_folders`. +fn process_paths_for_mod_files( + path: &Path, + folder_segments: &mut FxHashSet, + mod_folders: &mut FxHashSet, +) { + let mut comp = path.components().rev().peekable(); + let _ = comp.next(); + if path.ends_with("mod.rs") { + mod_folders.insert(comp.peek().map(|c| c.as_os_str().to_owned()).unwrap_or_default()); + } + let folders = comp + .filter_map(|c| { + if let Component::Normal(s) = c { + Some(s.to_os_string()) + } else { + None + } + }) + .collect::>(); + folder_segments.extend(folders); +} + +/// Checks every path for the presence of `mod.rs` files and emits the lint if found. +fn check_self_named_mod_exists(cx: &EarlyContext<'_>, path: &Path, file: &SourceFile) { + if path.ends_with("mod.rs") { + let mut mod_file = path.to_path_buf(); + mod_file.pop(); + mod_file.set_extension("rs"); + + cx.struct_span_lint( + MOD_MODULE_FILES, + Span::new(file.start_pos, file.start_pos, SyntaxContext::root()), + |build| { + let mut lint = build.build(&format!("`mod.rs` files are not allowed, found `{}`", path.display())); + lint.help(&format!("move `{}` to `{}`", path.display(), mod_file.display(),)); + lint.emit(); + }, + ); + } +} diff --git a/tests/ui-cargo/module_style/fail_mod/Cargo.toml b/tests/ui-cargo/module_style/fail_mod/Cargo.toml new file mode 100644 index 0000000000000..27b61c09fb48b --- /dev/null +++ b/tests/ui-cargo/module_style/fail_mod/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "fail" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/ui-cargo/module_style/fail_mod/src/bad/inner.rs b/tests/ui-cargo/module_style/fail_mod/src/bad/inner.rs new file mode 100644 index 0000000000000..91cd540a28fdd --- /dev/null +++ b/tests/ui-cargo/module_style/fail_mod/src/bad/inner.rs @@ -0,0 +1 @@ +pub mod stuff; diff --git a/tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff.rs b/tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff.rs new file mode 100644 index 0000000000000..7713fa9d35c4a --- /dev/null +++ b/tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff.rs @@ -0,0 +1,3 @@ +pub mod most; + +pub struct Inner; diff --git a/tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff/most.rs b/tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff/most.rs new file mode 100644 index 0000000000000..5a5eaf9670f90 --- /dev/null +++ b/tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff/most.rs @@ -0,0 +1 @@ +pub struct Snarks; diff --git a/tests/ui-cargo/module_style/fail_mod/src/bad/mod.rs b/tests/ui-cargo/module_style/fail_mod/src/bad/mod.rs new file mode 100644 index 0000000000000..a12734db7cb5f --- /dev/null +++ b/tests/ui-cargo/module_style/fail_mod/src/bad/mod.rs @@ -0,0 +1,3 @@ +pub mod inner; + +pub struct Thing; diff --git a/tests/ui-cargo/module_style/fail_mod/src/main.rs b/tests/ui-cargo/module_style/fail_mod/src/main.rs new file mode 100644 index 0000000000000..3e985d4e904c1 --- /dev/null +++ b/tests/ui-cargo/module_style/fail_mod/src/main.rs @@ -0,0 +1,9 @@ +#![warn(clippy::self_named_module_files)] + +mod bad; + +fn main() { + let _ = bad::Thing; + let _ = bad::inner::stuff::Inner; + let _ = bad::inner::stuff::most::Snarks; +} diff --git a/tests/ui-cargo/module_style/fail_mod/src/main.stderr b/tests/ui-cargo/module_style/fail_mod/src/main.stderr new file mode 100644 index 0000000000000..af4c298b31085 --- /dev/null +++ b/tests/ui-cargo/module_style/fail_mod/src/main.stderr @@ -0,0 +1,19 @@ +error: `mod.rs` files are required, found `/bad/inner.rs` + --> $DIR/bad/inner.rs:1:1 + | +LL | pub mod stuff; + | ^ + | + = note: `-D clippy::self-named-module-files` implied by `-D warnings` + = help: move `/bad/inner.rs` to `/bad/inner/mod.rs` + +error: `mod.rs` files are required, found `/bad/inner/stuff.rs` + --> $DIR/bad/inner/stuff.rs:1:1 + | +LL | pub mod most; + | ^ + | + = help: move `/bad/inner/stuff.rs` to `/bad/inner/stuff/mod.rs` + +error: aborting due to 2 previous errors + diff --git a/tests/ui-cargo/module_style/fail_no_mod/Cargo.toml b/tests/ui-cargo/module_style/fail_no_mod/Cargo.toml new file mode 100644 index 0000000000000..27b61c09fb48b --- /dev/null +++ b/tests/ui-cargo/module_style/fail_no_mod/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "fail" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/ui-cargo/module_style/fail_no_mod/src/bad/mod.rs b/tests/ui-cargo/module_style/fail_no_mod/src/bad/mod.rs new file mode 100644 index 0000000000000..f19ab10d5fb06 --- /dev/null +++ b/tests/ui-cargo/module_style/fail_no_mod/src/bad/mod.rs @@ -0,0 +1 @@ +pub struct Thing; diff --git a/tests/ui-cargo/module_style/fail_no_mod/src/main.rs b/tests/ui-cargo/module_style/fail_no_mod/src/main.rs new file mode 100644 index 0000000000000..c6e9045b8dc47 --- /dev/null +++ b/tests/ui-cargo/module_style/fail_no_mod/src/main.rs @@ -0,0 +1,7 @@ +#![warn(clippy::mod_module_files)] + +mod bad; + +fn main() { + let _ = bad::Thing; +} diff --git a/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr b/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr new file mode 100644 index 0000000000000..11e15db7fb96b --- /dev/null +++ b/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr @@ -0,0 +1,11 @@ +error: `mod.rs` files are not allowed, found `/bad/mod.rs` + --> $DIR/bad/mod.rs:1:1 + | +LL | pub struct Thing; + | ^ + | + = note: `-D clippy::mod-module-files` implied by `-D warnings` + = help: move `/bad/mod.rs` to `/bad.rs` + +error: aborting due to previous error + diff --git a/tests/ui-cargo/module_style/pass_mod/Cargo.toml b/tests/ui-cargo/module_style/pass_mod/Cargo.toml new file mode 100644 index 0000000000000..27b61c09fb48b --- /dev/null +++ b/tests/ui-cargo/module_style/pass_mod/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "fail" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/ui-cargo/module_style/pass_mod/src/bad/mod.rs b/tests/ui-cargo/module_style/pass_mod/src/bad/mod.rs new file mode 100644 index 0000000000000..f19ab10d5fb06 --- /dev/null +++ b/tests/ui-cargo/module_style/pass_mod/src/bad/mod.rs @@ -0,0 +1 @@ +pub struct Thing; diff --git a/tests/ui-cargo/module_style/pass_mod/src/main.rs b/tests/ui-cargo/module_style/pass_mod/src/main.rs new file mode 100644 index 0000000000000..9e08715fc0522 --- /dev/null +++ b/tests/ui-cargo/module_style/pass_mod/src/main.rs @@ -0,0 +1,10 @@ +#![warn(clippy::self_named_module_files)] + +mod bad; +mod more; + +fn main() { + let _ = bad::Thing; + let _ = more::foo::Foo; + let _ = more::inner::Inner; +} diff --git a/tests/ui-cargo/module_style/pass_mod/src/more/foo.rs b/tests/ui-cargo/module_style/pass_mod/src/more/foo.rs new file mode 100644 index 0000000000000..4a835673a596b --- /dev/null +++ b/tests/ui-cargo/module_style/pass_mod/src/more/foo.rs @@ -0,0 +1 @@ +pub struct Foo; diff --git a/tests/ui-cargo/module_style/pass_mod/src/more/inner/mod.rs b/tests/ui-cargo/module_style/pass_mod/src/more/inner/mod.rs new file mode 100644 index 0000000000000..aa84f78cc2ca1 --- /dev/null +++ b/tests/ui-cargo/module_style/pass_mod/src/more/inner/mod.rs @@ -0,0 +1 @@ +pub struct Inner; diff --git a/tests/ui-cargo/module_style/pass_mod/src/more/mod.rs b/tests/ui-cargo/module_style/pass_mod/src/more/mod.rs new file mode 100644 index 0000000000000..d79569f78ffbe --- /dev/null +++ b/tests/ui-cargo/module_style/pass_mod/src/more/mod.rs @@ -0,0 +1,2 @@ +pub mod foo; +pub mod inner; diff --git a/tests/ui-cargo/module_style/pass_no_mod/Cargo.toml b/tests/ui-cargo/module_style/pass_no_mod/Cargo.toml new file mode 100644 index 0000000000000..3c0896dd2cda7 --- /dev/null +++ b/tests/ui-cargo/module_style/pass_no_mod/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "pass" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/ui-cargo/module_style/pass_no_mod/src/good.rs b/tests/ui-cargo/module_style/pass_no_mod/src/good.rs new file mode 100644 index 0000000000000..f19ab10d5fb06 --- /dev/null +++ b/tests/ui-cargo/module_style/pass_no_mod/src/good.rs @@ -0,0 +1 @@ +pub struct Thing; diff --git a/tests/ui-cargo/module_style/pass_no_mod/src/main.rs b/tests/ui-cargo/module_style/pass_no_mod/src/main.rs new file mode 100644 index 0000000000000..50211a340b91b --- /dev/null +++ b/tests/ui-cargo/module_style/pass_no_mod/src/main.rs @@ -0,0 +1,7 @@ +#![warn(clippy::mod_module_files)] + +mod good; + +fn main() { + let _ = good::Thing; +} From 10909ae390a0a58e8742ba60b39a7cf907e8e5d3 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Wed, 25 Aug 2021 16:25:01 +0200 Subject: [PATCH 41/94] Updated issue templates(formatting and rustbot label reference) --- .github/ISSUE_TEMPLATE/blank_issue.md | 13 ++++++++ .github/ISSUE_TEMPLATE/bug_report.md | 39 +++++++++++------------- .github/ISSUE_TEMPLATE/false_negative.md | 22 ++++++------- .github/ISSUE_TEMPLATE/false_positive.md | 30 +++++++++++------- .github/ISSUE_TEMPLATE/ice.md | 21 ++++++------- 5 files changed, 70 insertions(+), 55 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/blank_issue.md b/.github/ISSUE_TEMPLATE/blank_issue.md index 9aef3ebe637a1..5d7dfa36f37a2 100644 --- a/.github/ISSUE_TEMPLATE/blank_issue.md +++ b/.github/ISSUE_TEMPLATE/blank_issue.md @@ -2,3 +2,16 @@ name: Blank Issue about: Create a blank issue. --- + + + diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 2bc87db123d4d..41e6abea1c6a5 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -20,28 +20,23 @@ Instead, this happened: *explanation* ### Meta -- `cargo clippy -V`: e.g. clippy 0.0.212 (f455e46 2020-06-20) -- `rustc -Vv`: - ``` - rustc 1.46.0-nightly (f455e46ea 2020-06-20) - binary: rustc - commit-hash: f455e46eae1a227d735091091144601b467e1565 - commit-date: 2020-06-20 - host: x86_64-unknown-linux-gnu - release: 1.46.0-nightly - LLVM version: 10.0 - ``` +**Rust version (`rustc -Vv`):** + +``` +rustc 1.46.0-nightly (f455e46ea 2020-06-20) +binary: rustc +commit-hash: f455e46eae1a227d735091091144601b467e1565 +commit-date: 2020-06-20 +host: x86_64-unknown-linux-gnu +release: 1.46.0-nightly +LLVM version: 10.0 +``` -
Backtrace -

- - ``` - - ``` - -

-
diff --git a/.github/ISSUE_TEMPLATE/false_negative.md b/.github/ISSUE_TEMPLATE/false_negative.md index 53341c7a928af..d9ea2db34edd7 100644 --- a/.github/ISSUE_TEMPLATE/false_negative.md +++ b/.github/ISSUE_TEMPLATE/false_negative.md @@ -22,14 +22,14 @@ Instead, this happened: *explanation* ### Meta -- `cargo clippy -V`: e.g. clippy 0.0.212 (f455e46 2020-06-20) -- `rustc -Vv`: - ``` - rustc 1.46.0-nightly (f455e46ea 2020-06-20) - binary: rustc - commit-hash: f455e46eae1a227d735091091144601b467e1565 - commit-date: 2020-06-20 - host: x86_64-unknown-linux-gnu - release: 1.46.0-nightly - LLVM version: 10.0 - ``` +**Rust version (`rustc -Vv`):** + +``` +rustc 1.46.0-nightly (f455e46ea 2020-06-20) +binary: rustc +commit-hash: f455e46eae1a227d735091091144601b467e1565 +commit-date: 2020-06-20 +host: x86_64-unknown-linux-gnu +release: 1.46.0-nightly +LLVM version: 10.0 +``` diff --git a/.github/ISSUE_TEMPLATE/false_positive.md b/.github/ISSUE_TEMPLATE/false_positive.md index 34fd6f4bdb710..edf9e04e3b1a2 100644 --- a/.github/ISSUE_TEMPLATE/false_positive.md +++ b/.github/ISSUE_TEMPLATE/false_positive.md @@ -22,14 +22,22 @@ Instead, this happened: *explanation* ### Meta -- `cargo clippy -V`: e.g. clippy 0.0.212 (f455e46 2020-06-20) -- `rustc -Vv`: - ``` - rustc 1.46.0-nightly (f455e46ea 2020-06-20) - binary: rustc - commit-hash: f455e46eae1a227d735091091144601b467e1565 - commit-date: 2020-06-20 - host: x86_64-unknown-linux-gnu - release: 1.46.0-nightly - LLVM version: 10.0 - ``` +**Rust version (`rustc -Vv`):** +``` +rustc 1.46.0-nightly (f455e46ea 2020-06-20) +binary: rustc +commit-hash: f455e46eae1a227d735091091144601b467e1565 +commit-date: 2020-06-20 +host: x86_64-unknown-linux-gnu +release: 1.46.0-nightly +LLVM version: 10.0 +``` + + diff --git a/.github/ISSUE_TEMPLATE/ice.md b/.github/ISSUE_TEMPLATE/ice.md index 0b7cd1ed0fb60..6c1bed663c6cb 100644 --- a/.github/ISSUE_TEMPLATE/ice.md +++ b/.github/ISSUE_TEMPLATE/ice.md @@ -20,17 +20,16 @@ http://blog.pnkfx.org/blog/2019/11/18/rust-bug-minimization-patterns/ ### Meta -- `cargo clippy -V`: e.g. clippy 0.0.212 (f455e46 2020-06-20) -- `rustc -Vv`: - ``` - rustc 1.46.0-nightly (f455e46ea 2020-06-20) - binary: rustc - commit-hash: f455e46eae1a227d735091091144601b467e1565 - commit-date: 2020-06-20 - host: x86_64-unknown-linux-gnu - release: 1.46.0-nightly - LLVM version: 10.0 - ``` +**Rust version (`rustc -Vv`):** +``` +rustc 1.46.0-nightly (f455e46ea 2020-06-20) +binary: rustc +commit-hash: f455e46eae1a227d735091091144601b467e1565 +commit-date: 2020-06-20 +host: x86_64-unknown-linux-gnu +release: 1.46.0-nightly +LLVM version: 10.0 +``` ### Error output From 4d627fc41b0a34d99afbaa21f3b8c301d942ffc8 Mon Sep 17 00:00:00 2001 From: lcnr Date: Sat, 13 Mar 2021 16:05:15 +0100 Subject: [PATCH 42/94] require a `tcx` for `TypeVisitor` --- clippy_lints/src/redundant_clone.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 530b3396abef6..2335d2248aa9a 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -14,7 +14,7 @@ use rustc_middle::mir::{ visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor as _}, Mutability, }; -use rustc_middle::ty::{self, fold::TypeVisitor, Ty}; +use rustc_middle::ty::{self, fold::TypeVisitor, Ty, TyCtxt}; use rustc_mir::dataflow::{Analysis, AnalysisDomain, GenKill, GenKillAnalysis, ResultsCursor}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::{BytePos, Span}; @@ -576,7 +576,7 @@ impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> { self.possible_borrower.add(borrowed.local, lhs); }, other => { - if ContainsRegion + if ContainsRegion(self.cx.tcx) .visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty) .is_continue() { @@ -625,7 +625,7 @@ impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> { .flat_map(HybridBitSet::iter) .collect(); - if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_break() { + if ContainsRegion(self.cx.tcx).visit_ty(self.body.local_decls[*dest].ty).is_break() { mutable_variables.push(*dest); } @@ -701,12 +701,15 @@ impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleOriginVisitor<'a, 'tcx> { } } -struct ContainsRegion; +struct ContainsRegion<'tcx>(TyCtxt<'tcx>); -impl TypeVisitor<'_> for ContainsRegion { +impl<'tcx> TypeVisitor<'tcx> for ContainsRegion<'tcx> { type BreakTy = (); + fn tcx_for_anon_const_substs(&self) -> TyCtxt<'tcx> { + self.0 + } - fn visit_region(&mut self, _: ty::Region<'_>) -> ControlFlow { + fn visit_region(&mut self, _: ty::Region<'tcx>) -> ControlFlow { ControlFlow::BREAK } } From 19d1fe21c75bcfda468611c38da518f98d6e2c32 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 16 Mar 2021 00:05:45 +0100 Subject: [PATCH 43/94] make unevaluated const substs optional --- clippy_lints/src/non_copy_const.rs | 6 +----- clippy_lints/src/redundant_clone.rs | 4 ++-- clippy_utils/src/consts.rs | 6 +----- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 3f9110295fc6b..2a85a67fa099c 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -187,11 +187,7 @@ fn is_value_unfrozen_expr<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId, def_id: D let result = cx.tcx.const_eval_resolve( cx.param_env, - ty::Unevaluated { - def: ty::WithOptConstParam::unknown(def_id), - substs, - promoted: None, - }, + ty::Unevaluated::new(ty::WithOptConstParam::unknown(def_id), substs), None, ); is_value_unfrozen_raw(cx, result, ty) diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 2335d2248aa9a..f5e43264a5c69 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -705,8 +705,8 @@ struct ContainsRegion<'tcx>(TyCtxt<'tcx>); impl<'tcx> TypeVisitor<'tcx> for ContainsRegion<'tcx> { type BreakTy = (); - fn tcx_for_anon_const_substs(&self) -> TyCtxt<'tcx> { - self.0 + fn tcx_for_anon_const_substs(&self) -> Option> { + Some(self.0) } fn visit_region(&mut self, _: ty::Region<'tcx>) -> ControlFlow { diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 9ba1381da6593..8bf31807d55d1 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -346,11 +346,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { .tcx .const_eval_resolve( self.param_env, - ty::Unevaluated { - def: ty::WithOptConstParam::unknown(def_id), - substs, - promoted: None, - }, + ty::Unevaluated::new(ty::WithOptConstParam::unknown(def_id), substs), None, ) .ok() From b1786f62edf8bc20de33a36973ffb307a13962f5 Mon Sep 17 00:00:00 2001 From: lcnr Date: Sat, 17 Jul 2021 16:43:23 +0200 Subject: [PATCH 44/94] add `tcx` to `fn walk` --- clippy_lints/src/escape.rs | 4 ++-- clippy_lints/src/let_underscore.rs | 2 +- clippy_lints/src/loops/same_item_push.rs | 2 +- clippy_lints/src/methods/mod.rs | 10 +++++----- clippy_lints/src/returns.rs | 2 +- clippy_lints/src/self_named_constructors.rs | 4 ++-- clippy_lints/src/unnecessary_sort_by.rs | 2 +- clippy_lints/src/use_self.rs | 2 +- clippy_utils/src/lib.rs | 4 ++-- clippy_utils/src/qualify_min_const_fn.rs | 2 +- clippy_utils/src/ty.rs | 12 ++++++------ 11 files changed, 23 insertions(+), 23 deletions(-) diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 8b0e9e6bc9b93..685dbf26250ce 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -53,7 +53,7 @@ fn is_non_trait_box(ty: Ty<'_>) -> bool { struct EscapeDelegate<'a, 'tcx> { cx: &'a LateContext<'tcx>, set: HirIdSet, - trait_self_ty: Option>, + trait_self_ty: Option>, too_large_for_stack: u64, } @@ -171,7 +171,7 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { // skip if there is a `self` parameter binding to a type // that contains `Self` (i.e.: `self: Box`), see #4804 if let Some(trait_self_ty) = self.trait_self_ty { - if map.name(cmt.hir_id) == kw::SelfLower && contains_ty(cmt.place.ty(), trait_self_ty) { + if map.name(cmt.hir_id) == kw::SelfLower && contains_ty(self.cx.tcx, cmt.place.ty(), trait_self_ty) { return; } } diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index 8992d25932cad..89146b4dd2c9b 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -119,7 +119,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { if let Some(init) = local.init; then { let init_ty = cx.typeck_results().expr_ty(init); - let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() { + let contains_sync_guard = init_ty.walk(cx.tcx).any(|inner| match inner.unpack() { GenericArgKind::Type(inner_ty) => { SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path)) }, diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index 0f6cd5de761f9..545498a10478d 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -49,7 +49,7 @@ pub(super) fn check<'tcx>( if same_item_push_visitor.should_lint(); if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push; let vec_ty = cx.typeck_results().expr_ty(vec); - let ty = vec_ty.walk().nth(1).unwrap().expect_ty(); + let ty = vec_ty.walk(cx.tcx).nth(1).unwrap().expect_ty(); if cx .tcx .lang_items() diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 91606ed3b2bb0..9626cf79dc129 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1987,10 +1987,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods { // walk the return type and check for Self (this does not check associated types) if let Some(self_adt) = self_ty.ty_adt_def() { - if contains_adt_constructor(ret_ty, self_adt) { + if contains_adt_constructor(cx.tcx, ret_ty, self_adt) { return; } - } else if contains_ty(ret_ty, self_ty) { + } else if contains_ty(cx.tcx, ret_ty, self_ty) { return; } @@ -2001,10 +2001,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() { // walk the associated type and check for Self if let Some(self_adt) = self_ty.ty_adt_def() { - if contains_adt_constructor(projection_predicate.ty, self_adt) { + if contains_adt_constructor(cx.tcx, projection_predicate.ty, self_adt) { return; } - } else if contains_ty(projection_predicate.ty, self_ty) { + } else if contains_ty(cx.tcx, projection_predicate.ty, self_ty) { return; } } @@ -2053,7 +2053,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if let TraitItemKind::Fn(_, _) = item.kind; let ret_ty = return_ty(cx, item.hir_id()); let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty(); - if !contains_ty(ret_ty, self_ty); + if !contains_ty(cx.tcx, ret_ty, self_ty); then { span_lint( diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index e153288aa58df..681baed8c3696 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -288,7 +288,7 @@ impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { .fn_sig(def_id) .output() .skip_binder() - .walk() + .walk(self.cx.tcx) .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); } diff --git a/clippy_lints/src/self_named_constructors.rs b/clippy_lints/src/self_named_constructors.rs index 4472edecbed86..4ba5e1a0f5357 100644 --- a/clippy_lints/src/self_named_constructors.rs +++ b/clippy_lints/src/self_named_constructors.rs @@ -62,10 +62,10 @@ impl<'tcx> LateLintPass<'tcx> for SelfNamedConstructors { // Ensure method is constructor-like if let Some(self_adt) = self_ty.ty_adt_def() { - if !contains_adt_constructor(ret_ty, self_adt) { + if !contains_adt_constructor(cx.tcx, ret_ty, self_adt) { return; } - } else if !contains_ty(ret_ty, self_ty) { + } else if !contains_ty(cx.tcx, ret_ty, self_ty) { return; } diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs index 6fc5707a4eeff..97b1b2dae3c1a 100644 --- a/clippy_lints/src/unnecessary_sort_by.rs +++ b/clippy_lints/src/unnecessary_sort_by.rs @@ -218,7 +218,7 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(expr); - matches!(ty.kind(), ty::Ref(..)) || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))) + matches!(ty.kind(), ty::Ref(..)) || ty.walk(cx.tcx).any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))) } impl LateLintPass<'_> for UnnecessarySortBy { diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index a3601cca2eff1..9ae50e47ca4c5 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -170,7 +170,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { // // See also https://github.com/rust-lang/rust-clippy/issues/2894. for (impl_hir_ty, trait_sem_ty) in impl_inputs_outputs.zip(trait_method_sig.inputs_and_output) { - if trait_sem_ty.walk().any(|inner| inner == self_ty.into()) { + if trait_sem_ty.walk(cx.tcx).any(|inner| inner == self_ty.into()) { let mut visitor = SkipTyCollector::default(); visitor.visit_ty(impl_hir_ty); types_to_skip.extend(visitor.types_to_skip); diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 32a73984674ad..98f3937ba3ddb 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1627,7 +1627,7 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option return Some("slice".into()), rustc_ty::Array(..) => return Some("array".into()), rustc_ty::Tuple(..) => return Some("tuple".into()), @@ -1635,7 +1635,7 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option, body: &'a Body<'tcx>, msrv: Option<&Ru } fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult { - for arg in ty.walk() { + for arg in ty.walk(tcx) { let ty = match arg.unpack() { GenericArgKind::Type(ty) => ty, diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index a2221a0b283b0..3cd8ed5aa2c8c 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -10,7 +10,7 @@ use rustc_hir::{TyKind, Unsafety}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; -use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TypeFoldable, UintTy}; +use rustc_middle::ty::{self, TyCtxt, AdtDef, IntTy, Ty, TypeFoldable, UintTy}; use rustc_span::sym; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::DUMMY_SP; @@ -36,8 +36,8 @@ pub fn can_partially_move_ty(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { } /// Walks into `ty` and returns `true` if any inner type is the same as `other_ty` -pub fn contains_ty(ty: Ty<'_>, other_ty: Ty<'_>) -> bool { - ty.walk().any(|inner| match inner.unpack() { +pub fn contains_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, other_ty: Ty<'tcx>) -> bool { + ty.walk(tcx).any(|inner| match inner.unpack() { GenericArgKind::Type(inner_ty) => ty::TyS::same_type(other_ty, inner_ty), GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, }) @@ -45,8 +45,8 @@ pub fn contains_ty(ty: Ty<'_>, other_ty: Ty<'_>) -> bool { /// Walks into `ty` and returns `true` if any inner type is an instance of the given adt /// constructor. -pub fn contains_adt_constructor(ty: Ty<'_>, adt: &AdtDef) -> bool { - ty.walk().any(|inner| match inner.unpack() { +pub fn contains_adt_constructor<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, adt: &'tcx AdtDef) -> bool { + ty.walk(tcx).any(|inner| match inner.unpack() { GenericArgKind::Type(inner_ty) => inner_ty.ty_adt_def() == Some(adt), GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, }) @@ -209,7 +209,7 @@ fn is_normalizable_helper<'tcx>( .iter() .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache)) }), - _ => ty.walk().all(|generic_arg| match generic_arg.unpack() { + _ => ty.walk(cx.tcx).all(|generic_arg| match generic_arg.unpack() { GenericArgKind::Type(inner_ty) if inner_ty != ty => { is_normalizable_helper(cx, param_env, inner_ty, cache) }, From afd892a549bb4b9bdbe676bc0b3f9899a750d0a3 Mon Sep 17 00:00:00 2001 From: lcnr Date: Sat, 17 Jul 2021 18:48:07 +0200 Subject: [PATCH 45/94] update `TypeFlags` to deal with missing ct substs --- clippy_lints/src/needless_pass_by_value.rs | 2 +- clippy_utils/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 5e559991c1697..90b2aa1689626 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -117,7 +117,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { let fn_def_id = cx.tcx.hir().local_def_id(hir_id); let preds = traits::elaborate_predicates(cx.tcx, cx.param_env.caller_bounds().iter()) - .filter(|p| !p.is_global()) + .filter(|p| !p.is_global(cx.tcx)) .filter_map(|obligation| { // Note that we do not want to deal with qualified predicates here. match obligation.predicate.kind().no_bound_vars() { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 98f3937ba3ddb..ddff1686ba2ce 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1581,7 +1581,7 @@ pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool { .predicates_of(did) .predicates .iter() - .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None }); + .filter_map(|(p, _)| if p.is_global(cx.tcx) { Some(*p) } else { None }); traits::impossible_predicates( cx.tcx, traits::elaborate_predicates(cx.tcx, predicates) From c6e420266a5a416f54124b0db556434330dd0567 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 26 Aug 2021 10:01:11 +0100 Subject: [PATCH 46/94] Set applicability of needless_collect to MaybeIncorrect --- clippy_lints/src/loops/needless_collect.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index 51d7def137e40..f90ed7397e18e 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -26,7 +26,7 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont if chain_method.ident.name == sym!(collect) && is_trait_method(cx, &args[0], sym::Iterator); then { let ty = cx.typeck_results().expr_ty(&args[0]); - let mut applicability = Applicability::MachineApplicable; + let mut applicability = Applicability::MaybeIncorrect; let is_empty_sugg = "next().is_none()".to_string(); let method_name = &*method.ident.name.as_str(); let sugg = if is_type_diagnostic_item(cx, ty, sym::vec_type) || @@ -113,7 +113,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo (stmt.span, String::new()), (iter_call.span, iter_replacement) ], - Applicability::MachineApplicable,// MaybeIncorrect, + Applicability::MaybeIncorrect, ); }, ); From 86e92c00335672ed1aa3b9e2d7698a464823d118 Mon Sep 17 00:00:00 2001 From: dswij Date: Wed, 18 Aug 2021 12:40:59 +0800 Subject: [PATCH 47/94] Update common tool docs on type implement trait check Common tool documentation uses `match_trait_method` which is deprecated. Additionally, adds information on why `is_trait_method` is not the preferred way. --- doc/common_tools_writing_lints.md | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/doc/common_tools_writing_lints.md b/doc/common_tools_writing_lints.md index 0a85f65001101..45f13de29a32d 100644 --- a/doc/common_tools_writing_lints.md +++ b/doc/common_tools_writing_lints.md @@ -75,20 +75,21 @@ impl LateLintPass<'_> for MyStructLint { # Checking if a type implements a specific trait -There are two ways to do this, depending if the target trait is part of lang items. +There are three ways to do this, depending on if the target trait has a diagnostic item, lang item or neither. ```rust -use clippy_utils::{implements_trait, match_trait_method, paths}; +use clippy_utils::{implements_trait, is_trait_method, match_trait_method}; +use rustc_span::symbol::sym; impl LateLintPass<'_> for MyStructLint { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - // 1. Using expression and Clippy's convenient method - // we use `match_trait_method` function from Clippy's toolbox - if match_trait_method(cx, expr, &paths::INTO) { - // `expr` implements `Into` trait + // 1. Using diagnostic items with the expression + // we use `is_trait_method` function from Clippy's utils + if is_trait_method(cx, expr, sym::Iterator) { + // method call in `expr` belongs to `Iterator` trait } - // 2. Using type context `TyCtxt` + // 2. Using lang items with the expression type let ty = cx.typeck_results().expr_ty(expr); if cx.tcx.lang_items() // we are looking for the `DefId` of `Drop` trait in lang items @@ -97,15 +98,20 @@ impl LateLintPass<'_> for MyStructLint { .map_or(false, |id| implements_trait(cx, ty, id, &[])) { // `expr` implements `Drop` trait } + + // 3. Using the type path with the expression + // we use `match_trait_method` function from Clippy's utils + if match_trait_method(cx, expr, &paths::INTO) { + // `expr` implements `Into` trait + } } } ``` -> Prefer using lang items, if the target trait is available there. - -A list of defined paths for Clippy can be found in [paths.rs][paths] +> Prefer using diagnostic and lang items, if the target trait has one. We access lang items through the type context `tcx`. `tcx` is of type [`TyCtxt`][TyCtxt] and is defined in the `rustc_middle` crate. +A list of defined paths for Clippy can be found in [paths.rs][paths] # Checking if a type defines a specific method From f0cb8a789acfac8a25e60e4e4e791e4c51ecf1ee Mon Sep 17 00:00:00 2001 From: dswij <44697459+dswij@users.noreply.github.com> Date: Thu, 26 Aug 2021 18:01:41 +0800 Subject: [PATCH 48/94] Update doc/common_tools_writing_lints.md Add missing import `paths` for the doc example. Co-authored-by: Fridtjof Stoldt --- doc/common_tools_writing_lints.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/common_tools_writing_lints.md b/doc/common_tools_writing_lints.md index 45f13de29a32d..5f45951090e7e 100644 --- a/doc/common_tools_writing_lints.md +++ b/doc/common_tools_writing_lints.md @@ -78,7 +78,7 @@ impl LateLintPass<'_> for MyStructLint { There are three ways to do this, depending on if the target trait has a diagnostic item, lang item or neither. ```rust -use clippy_utils::{implements_trait, is_trait_method, match_trait_method}; +use clippy_utils::{implements_trait, is_trait_method, match_trait_method, paths}; use rustc_span::symbol::sym; impl LateLintPass<'_> for MyStructLint { From 3e5dcba2ee446ac712a4a57b042ca4b7f6a46ecc Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 26 Aug 2021 11:27:43 -0400 Subject: [PATCH 49/94] Small cleanup of `option_if_let_else` and additional tests. --- clippy_lints/src/option_if_let_else.rs | 6 +++--- tests/ui/option_if_let_else.fixed | 20 ++++++++++++++++++ tests/ui/option_if_let_else.rs | 20 ++++++++++++++++++ tests/ui/option_if_let_else.stderr | 28 +++++++++++++------------- 4 files changed, 57 insertions(+), 17 deletions(-) diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 46f06362ccc67..0614c14462b7f 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -174,9 +174,9 @@ fn detect_option_if_let_else<'tcx>( ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e), _ => None, }); - if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind { - match some_captures.get(l) - .or_else(|| (method_sugg == "map_or_else").then(|| ()).and_then(|_| none_captures.get(l))) + if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(local_id), .. })) = e.kind { + match some_captures.get(local_id) + .or_else(|| (method_sugg == "map_or_else").then(|| ()).and_then(|_| none_captures.get(local_id))) { Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None, Some(CaptureKind::Ref(Mutability::Not)) if as_mut => return None, diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index 56e032cdb57aa..d1815d0aec331 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -1,3 +1,4 @@ +// edition:2018 // run-rustfix #![warn(clippy::option_if_let_else)] #![allow(clippy::redundant_closure)] @@ -128,4 +129,23 @@ fn main() { let _s = s; 10 }; + + let mut s = Some(String::new()); + // Don't lint, `Some` branch mutably borrows `s`, but else branch also borrows `s` + let _ = if let Some(x) = &mut s { + x.push_str("test"); + x.len() + } else { + let _s = &s; + 10 + }; + + async fn _f1(x: u32) -> u32 { + x + } + + async fn _f2() { + // Don't lint. `await` can't be moved into a closure. + let _ = if let Some(x) = Some(0) { _f1(x).await } else { 0 }; + } } diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index 595ed8172cd0e..a15627338cb4a 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -1,3 +1,4 @@ +// edition:2018 // run-rustfix #![warn(clippy::option_if_let_else)] #![allow(clippy::redundant_closure)] @@ -153,4 +154,23 @@ fn main() { let _s = s; 10 }; + + let mut s = Some(String::new()); + // Don't lint, `Some` branch mutably borrows `s`, but else branch also borrows `s` + let _ = if let Some(x) = &mut s { + x.push_str("test"); + x.len() + } else { + let _s = &s; + 10 + }; + + async fn _f1(x: u32) -> u32 { + x + } + + async fn _f2() { + // Don't lint. `await` can't be moved into a closure. + let _ = if let Some(x) = Some(0) { _f1(x).await } else { 0 }; + } } diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index 803d941c36df8..ed748ee8b39e4 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:7:5 + --> $DIR/option_if_let_else.rs:8: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:25:13 + --> $DIR/option_if_let_else.rs:26: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:26:13 + --> $DIR/option_if_let_else.rs:27: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:27:13 + --> $DIR/option_if_let_else.rs:28: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:33:13 + --> $DIR/option_if_let_else.rs:34: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:34:13 + --> $DIR/option_if_let_else.rs:35: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:40:13 + --> $DIR/option_if_let_else.rs:41: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:49:5 + --> $DIR/option_if_let_else.rs:50: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:62:13 + --> $DIR/option_if_let_else.rs:63: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:71:13 + --> $DIR/option_if_let_else.rs:72:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -143,13 +143,13 @@ LL ~ }, |x| x * x * x * x); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:100:13 + --> $DIR/option_if_let_else.rs:101: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:109:13 + --> $DIR/option_if_let_else.rs:110:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -171,13 +171,13 @@ LL ~ }); | error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:137:13 + --> $DIR/option_if_let_else.rs:138:13 | LL | let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or_else(|| s.len(), |x| s.len() + x)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:141:13 + --> $DIR/option_if_let_else.rs:142:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ From c6c1f328ebedabbe1779a9a0543b9f6ba2696e8a Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Thu, 26 Aug 2021 12:46:01 +0200 Subject: [PATCH 50/94] Path remapping: Make behavior of diagnostics output dependent on presence of --remap-path-prefix. --- clippy_lints/src/macro_use.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index a371f8bbd3cb4..39f7ade3f81f6 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -47,11 +47,8 @@ pub struct MacroRefData { impl MacroRefData { pub fn new(name: String, callee: Span, cx: &LateContext<'_>) -> Self { - let mut path = cx - .sess() - .source_map() - .span_to_filename(callee) - .prefer_local() + let sm = cx.sess().source_map(); + let mut path = sm.filename_for_diagnostics(&sm.span_to_filename(callee)) .to_string(); // std lib paths are <::std::module::file type> From 8c4056fd4ad53d2a4ac7f7c7125c6c70f87c45c9 Mon Sep 17 00:00:00 2001 From: inquisitivecrystal <22333129+inquisitivecrystal@users.noreply.github.com> Date: Fri, 30 Jul 2021 23:50:57 -0700 Subject: [PATCH 51/94] Treat macros as HIR items --- clippy_lints/src/missing_doc.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index d358e9fb876a7..da86d28ee0b2d 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -123,6 +123,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { hir::ItemKind::Const(..) | hir::ItemKind::Enum(..) | hir::ItemKind::Mod(..) + | hir::ItemKind::Macro(..) | hir::ItemKind::Static(..) | hir::ItemKind::Struct(..) | hir::ItemKind::Trait(..) From c8262ade904889a854ec233048b5046c4070f164 Mon Sep 17 00:00:00 2001 From: inquisitivecrystal <22333129+inquisitivecrystal@users.noreply.github.com> Date: Thu, 5 Aug 2021 16:58:46 -0700 Subject: [PATCH 52/94] Teach tools that macros are now HIR items --- clippy_lints/src/missing_doc.rs | 2 +- clippy_lints/src/missing_inline.rs | 1 + clippy_lints/src/utils/inspector.rs | 7 +++++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index da86d28ee0b2d..940eee7a78897 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -122,8 +122,8 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { }, hir::ItemKind::Const(..) | hir::ItemKind::Enum(..) - | hir::ItemKind::Mod(..) | hir::ItemKind::Macro(..) + | hir::ItemKind::Mod(..) | hir::ItemKind::Static(..) | hir::ItemKind::Struct(..) | hir::ItemKind::Trait(..) diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index 977e6d966e873..667cdd8302528 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -118,6 +118,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { }, hir::ItemKind::Const(..) | hir::ItemKind::Enum(..) + | hir::ItemKind::Macro(..) | hir::ItemKind::Mod(..) | hir::ItemKind::Static(..) | hir::ItemKind::Struct(..) diff --git a/clippy_lints/src/utils/inspector.rs b/clippy_lints/src/utils/inspector.rs index 6bf216cec1670..e97983a2e1451 100644 --- a/clippy_lints/src/utils/inspector.rs +++ b/clippy_lints/src/utils/inspector.rs @@ -381,6 +381,13 @@ fn print_item(cx: &LateContext<'_>, item: &hir::Item<'_>) { 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), From 0b526fd7fb58ae20deada3fad050196cd838ee47 Mon Sep 17 00:00:00 2001 From: Ellen Date: Wed, 25 Aug 2021 10:21:39 +0100 Subject: [PATCH 53/94] rename const_evaluatable_checked to generic_const_exprs :sparkles: --- tests/ui/doc/doc.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/doc/doc.rs b/tests/ui/doc/doc.rs index 8afef6b23d476..8b20997fdf8d8 100644 --- a/tests/ui/doc/doc.rs +++ b/tests/ui/doc/doc.rs @@ -2,7 +2,7 @@ #![allow(dead_code, incomplete_features)] #![warn(clippy::doc_markdown)] -#![feature(custom_inner_attributes, const_generics, const_evaluatable_checked, const_option)] +#![feature(custom_inner_attributes, const_generics, generic_const_exprs, const_option)] #![rustfmt::skip] /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there) @@ -203,7 +203,7 @@ fn issue_2343() {} /// __|_ _|__||_| fn pulldown_cmark_crash() {} -// issue #7033 - const_evaluatable_checked ICE +// issue #7033 - generic_const_exprs ICE struct S where [(); N.checked_next_power_of_two().unwrap()]: { arr: [T; N.checked_next_power_of_two().unwrap()], From fd8b150959e63fca84e0c759d5a89e9249fb51c1 Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 27 Aug 2021 18:04:57 +0200 Subject: [PATCH 54/94] `feature(const_generics)` -> `feature(const_param_types)` --- tests/ui/crashes/ice-4775.rs | 3 --- tests/ui/crashes/ice-5223.rs | 3 --- tests/ui/doc/doc.rs | 2 +- tests/ui/missing_const_for_fn/cant_be_const.rs | 3 +-- .../ui/missing_const_for_fn/could_be_const.rs | 1 - .../missing_const_for_fn/could_be_const.stderr | 18 +++++++++--------- 6 files changed, 11 insertions(+), 19 deletions(-) diff --git a/tests/ui/crashes/ice-4775.rs b/tests/ui/crashes/ice-4775.rs index 31e53e846d54d..405e3039e7d0c 100644 --- a/tests/ui/crashes/ice-4775.rs +++ b/tests/ui/crashes/ice-4775.rs @@ -1,6 +1,3 @@ -#![feature(const_generics)] -#![allow(incomplete_features)] - pub struct ArrayWrapper([usize; N]); impl ArrayWrapper<{ N }> { diff --git a/tests/ui/crashes/ice-5223.rs b/tests/ui/crashes/ice-5223.rs index 9bb2e227fc12e..e3b3b27a6fc38 100644 --- a/tests/ui/crashes/ice-5223.rs +++ b/tests/ui/crashes/ice-5223.rs @@ -1,7 +1,4 @@ // Regression test for #5233 - -#![feature(const_generics)] -#![allow(incomplete_features)] #![warn(clippy::indexing_slicing, clippy::iter_cloned_collect)] pub struct KotomineArray { diff --git a/tests/ui/doc/doc.rs b/tests/ui/doc/doc.rs index 8b20997fdf8d8..8b0c0f304fce0 100644 --- a/tests/ui/doc/doc.rs +++ b/tests/ui/doc/doc.rs @@ -2,7 +2,7 @@ #![allow(dead_code, incomplete_features)] #![warn(clippy::doc_markdown)] -#![feature(custom_inner_attributes, const_generics, generic_const_exprs, const_option)] +#![feature(custom_inner_attributes, generic_const_exprs, const_option)] #![rustfmt::skip] /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there) diff --git a/tests/ui/missing_const_for_fn/cant_be_const.rs b/tests/ui/missing_const_for_fn/cant_be_const.rs index 6d2cbb6ad96fa..aa60d0504e5e6 100644 --- a/tests/ui/missing_const_for_fn/cant_be_const.rs +++ b/tests/ui/missing_const_for_fn/cant_be_const.rs @@ -5,8 +5,7 @@ // aux-build:helper.rs #![warn(clippy::missing_const_for_fn)] -#![allow(incomplete_features)] -#![feature(start, const_generics)] +#![feature(start)] #![feature(custom_inner_attributes)] extern crate helper; diff --git a/tests/ui/missing_const_for_fn/could_be_const.rs b/tests/ui/missing_const_for_fn/could_be_const.rs index 0accb516f5f6b..baa7eec05462e 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/tests/ui/missing_const_for_fn/could_be_const.rs @@ -1,6 +1,5 @@ #![warn(clippy::missing_const_for_fn)] #![allow(incomplete_features, clippy::let_and_return)] -#![feature(const_generics)] #![feature(custom_inner_attributes)] use std::mem::transmute; diff --git a/tests/ui/missing_const_for_fn/could_be_const.stderr b/tests/ui/missing_const_for_fn/could_be_const.stderr index 63c211f39fa1e..b89cc6451bb59 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.stderr +++ b/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -1,5 +1,5 @@ error: this could be a `const fn` - --> $DIR/could_be_const.rs:14:5 + --> $DIR/could_be_const.rs:13:5 | LL | / pub fn new() -> Self { LL | | Self { guess: 42 } @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::missing-const-for-fn` implied by `-D warnings` error: this could be a `const fn` - --> $DIR/could_be_const.rs:18:5 + --> $DIR/could_be_const.rs:17:5 | LL | / fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] { LL | | b @@ -17,7 +17,7 @@ LL | | } | |_____^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:24:1 + --> $DIR/could_be_const.rs:23:1 | LL | / fn one() -> i32 { LL | | 1 @@ -25,7 +25,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:29:1 + --> $DIR/could_be_const.rs:28:1 | LL | / fn two() -> i32 { LL | | let abc = 2; @@ -34,7 +34,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:35:1 + --> $DIR/could_be_const.rs:34:1 | LL | / fn string() -> String { LL | | String::new() @@ -42,7 +42,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:40:1 + --> $DIR/could_be_const.rs:39:1 | LL | / unsafe fn four() -> i32 { LL | | 4 @@ -50,7 +50,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:45:1 + --> $DIR/could_be_const.rs:44:1 | LL | / fn generic(t: T) -> T { LL | | t @@ -58,7 +58,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:68:9 + --> $DIR/could_be_const.rs:67:9 | LL | / pub fn b(self, a: &A) -> B { LL | | B @@ -66,7 +66,7 @@ LL | | } | |_________^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:78:5 + --> $DIR/could_be_const.rs:77:5 | LL | / fn const_fn_stabilized_before_msrv(byte: u8) { LL | | byte.is_ascii_digit(); From 78bf4acc3a18848e21896bae97859c5811b320d4 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sun, 25 Jul 2021 18:27:44 -0500 Subject: [PATCH 55/94] Fix clippy for let-else --- clippy_lints/src/non_expressive_names.rs | 7 +++++-- clippy_utils/src/ast_utils.rs | 12 +++++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index ac21eb5275f0f..2ffc00b449d08 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -316,8 +316,11 @@ impl<'a, 'b> SimilarNamesLocalVisitor<'a, 'b> { impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> { fn visit_local(&mut self, local: &'tcx Local) { - if let Some(ref init) = local.init { - self.apply(|this| walk_expr(this, &**init)); + if let Some((init, els)) = &local.kind.init_else_opt() { + self.apply(|this| walk_expr(this, init)); + if let Some(els) = els { + self.apply(|this| walk_block(this, els)); + } } // add the pattern after the expression because the bindings aren't available // yet in the init diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index 7ea07a15aea51..133f6c29f7d21 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -221,7 +221,7 @@ pub fn eq_stmt(l: &Stmt, r: &Stmt) -> bool { (Local(l), Local(r)) => { eq_pat(&l.pat, &r.pat) && both(&l.ty, &r.ty, |l, r| eq_ty(l, r)) - && eq_expr_opt(&l.init, &r.init) + && eq_local_kind(&l.kind, &r.kind) && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) }, (Item(l), Item(r)) => eq_item(l, r, eq_item_kind), @@ -234,6 +234,16 @@ pub fn eq_stmt(l: &Stmt, r: &Stmt) -> bool { } } +pub fn eq_local_kind(l: &LocalKind, r: &LocalKind) -> bool { + use LocalKind::*; + match (l, r) { + (Decl, Decl) => true, + (Init(l), Init(r)) => eq_expr(l, r), + (InitElse(li, le), InitElse(ri, re)) => eq_expr(li, ri) && eq_block(le, re), + _ => false, + } +} + pub fn eq_item(l: &Item, r: &Item, mut eq_kind: impl FnMut(&K, &K) -> bool) -> bool { eq_id(l.ident, r.ident) && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) From f75d1b2bc5b5794ccc43925f36900d404211eeda Mon Sep 17 00:00:00 2001 From: xordi Date: Thu, 26 Aug 2021 16:14:37 +0200 Subject: [PATCH 56/94] Check for Not trait implementation --- clippy_lints/src/bool_assert_comparison.rs | 90 +++++++++++++++------- clippy_lints/src/lib.rs | 2 +- 2 files changed, 63 insertions(+), 29 deletions(-) diff --git a/clippy_lints/src/bool_assert_comparison.rs b/clippy_lints/src/bool_assert_comparison.rs index 8d3f68565b223..171d22f998666 100644 --- a/clippy_lints/src/bool_assert_comparison.rs +++ b/clippy_lints/src/bool_assert_comparison.rs @@ -1,9 +1,11 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{ast_utils, is_direct_expn_of}; -use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind}; +use clippy_utils::{diagnostics::span_lint_and_sugg, higher, is_direct_expn_of, ty::implements_trait}; +use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_hir::*; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::Ident; declare_clippy_lint! { /// ### What it does @@ -28,45 +30,77 @@ declare_clippy_lint! { declare_lint_pass!(BoolAssertComparison => [BOOL_ASSERT_COMPARISON]); -fn is_bool_lit(e: &Expr) -> bool { +fn is_bool_lit(e: &Expr<'_>) -> bool { matches!( e.kind, ExprKind::Lit(Lit { - kind: LitKind::Bool(_), + node: LitKind::Bool(_), .. }) ) && !e.span.from_expansion() } -impl EarlyLintPass for BoolAssertComparison { - fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) { +fn impl_not_trait_with_bool_out(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { + let ty = cx.typeck_results().expr_ty(e); + + cx.tcx + .lang_items() + .not_trait() + .filter(|id| implements_trait(cx, ty, *id, &[])) + .and_then(|id| { + cx.tcx.associated_items(id).find_by_name_and_kind( + cx.tcx, + Ident::from_str("Output"), + ty::AssocKind::Type, + id, + ) + }) + .map_or(false, |item| { + let proj = cx.tcx.mk_projection(item.def_id, cx.tcx.mk_substs_trait(ty, &[])); + let nty = cx.tcx.normalize_erasing_regions(cx.param_env, proj); + + nty.is_bool() + }) +} + +impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let macros = ["assert_eq", "debug_assert_eq"]; let inverted_macros = ["assert_ne", "debug_assert_ne"]; for mac in macros.iter().chain(inverted_macros.iter()) { - if let Some(span) = is_direct_expn_of(e.span, mac) { - if let Some([a, b]) = ast_utils::extract_assert_macro_args(e) { - let nb_bool_args = is_bool_lit(a) as usize + is_bool_lit(b) as usize; + if let Some(span) = is_direct_expn_of(expr.span, mac) { + if let Some(args) = higher::extract_assert_macro_args(expr) { + if let [a, b, ..] = args[..] { + let nb_bool_args = is_bool_lit(a) as usize + is_bool_lit(b) as usize; + + if nb_bool_args != 1 { + // If there are two boolean arguments, we definitely don't understand + // what's going on, so better leave things as is... + // + // Or there is simply no boolean and then we can leave things as is! + return; + } - if nb_bool_args != 1 { - // If there are two boolean arguments, we definitely don't understand - // what's going on, so better leave things as is... - // - // Or there is simply no boolean and then we can leave things as is! + if !impl_not_trait_with_bool_out(cx, a) || !impl_not_trait_with_bool_out(cx, b) { + // At this point the expression which is not a boolean + // literal does not implement Not trait with a bool output, + // so we cannot suggest to rewrite our code + return; + } + + let non_eq_mac = &mac[..mac.len() - 3]; + span_lint_and_sugg( + cx, + BOOL_ASSERT_COMPARISON, + span, + &format!("used `{}!` with a literal bool", mac), + "replace it with", + format!("{}!(..)", non_eq_mac), + Applicability::MaybeIncorrect, + ); return; } - - let non_eq_mac = &mac[..mac.len() - 3]; - span_lint_and_sugg( - cx, - BOOL_ASSERT_COMPARISON, - span, - &format!("used `{}!` with a literal bool", mac), - "replace it with", - format!("{}!(..)", non_eq_mac), - Applicability::MaybeIncorrect, - ); - return; } } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index be97776187fff..5987f9e5b0cb8 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -2115,7 +2115,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box from_str_radix_10::FromStrRadix10); store.register_late_pass(|| box manual_map::ManualMap); store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv)); - store.register_early_pass(|| box bool_assert_comparison::BoolAssertComparison); + store.register_late_pass(|| box bool_assert_comparison::BoolAssertComparison); store.register_early_pass(move || box module_style::ModStyle); store.register_late_pass(|| box unused_async::UnusedAsync); let disallowed_types = conf.disallowed_types.iter().cloned().collect::>(); From abbcea86ffc5972f4489d55afed39390ec68f670 Mon Sep 17 00:00:00 2001 From: xordi Date: Thu, 26 Aug 2021 16:15:04 +0200 Subject: [PATCH 57/94] Add new ui tests --- tests/ui/bool_assert_comparison.rs | 63 ++++++++++++++++++++++++++ tests/ui/bool_assert_comparison.stderr | 62 +++++++++++++++++-------- 2 files changed, 106 insertions(+), 19 deletions(-) diff --git a/tests/ui/bool_assert_comparison.rs b/tests/ui/bool_assert_comparison.rs index 2de402fae8c7d..3961b9432a302 100644 --- a/tests/ui/bool_assert_comparison.rs +++ b/tests/ui/bool_assert_comparison.rs @@ -1,5 +1,7 @@ #![warn(clippy::bool_assert_comparison)] +use std::ops::Not; + macro_rules! a { () => { true @@ -11,7 +13,58 @@ macro_rules! b { }; } +// Implements the Not trait but with an output type +// that's not bool. Should not suggest a rewrite +#[derive(Debug)] +enum A { + VariantX(bool), + VariantY(u32), +} + +impl PartialEq for A { + fn eq(&self, other: &bool) -> bool { + match *self { + A::VariantX(b) => b == *other, + _ => false, + } + } +} + +impl Not for A { + type Output = Self; + + fn not(self) -> Self::Output { + match self { + A::VariantX(b) => A::VariantX(!b), + A::VariantY(0) => A::VariantY(1), + A::VariantY(_) => A::VariantY(0), + } + } +} + +// This type implements the Not trait with an Output of +// type bool. Using assert!(..) must be suggested +#[derive(Debug)] +struct B; + +impl PartialEq for B { + fn eq(&self, other: &bool) -> bool { + false + } +} + +impl Not for B { + type Output = bool; + + fn not(self) -> Self::Output { + true + } +} + fn main() { + let a = A::VariantX(true); + let b = B {}; + assert_eq!("a".len(), 1); assert_eq!("a".is_empty(), false); assert_eq!("".is_empty(), true); @@ -19,6 +72,8 @@ fn main() { assert_eq!(a!(), b!()); assert_eq!(a!(), "".is_empty()); assert_eq!("".is_empty(), b!()); + assert_eq!(a, true); + assert_eq!(b, true); assert_ne!("a".len(), 1); assert_ne!("a".is_empty(), false); @@ -27,6 +82,8 @@ fn main() { assert_ne!(a!(), b!()); assert_ne!(a!(), "".is_empty()); assert_ne!("".is_empty(), b!()); + assert_ne!(a, true); + assert_ne!(b, true); debug_assert_eq!("a".len(), 1); debug_assert_eq!("a".is_empty(), false); @@ -35,6 +92,8 @@ fn main() { debug_assert_eq!(a!(), b!()); debug_assert_eq!(a!(), "".is_empty()); debug_assert_eq!("".is_empty(), b!()); + debug_assert_eq!(a, true); + debug_assert_eq!(b, true); debug_assert_ne!("a".len(), 1); debug_assert_ne!("a".is_empty(), false); @@ -43,6 +102,8 @@ fn main() { debug_assert_ne!(a!(), b!()); debug_assert_ne!(a!(), "".is_empty()); debug_assert_ne!("".is_empty(), b!()); + debug_assert_ne!(a, true); + debug_assert_ne!(b, true); // assert with error messages assert_eq!("a".len(), 1, "tadam {}", 1); @@ -50,10 +111,12 @@ fn main() { assert_eq!("a".is_empty(), false, "tadam {}", 1); assert_eq!("a".is_empty(), false, "tadam {}", true); assert_eq!(false, "a".is_empty(), "tadam {}", true); + assert_eq!(a, true, "tadam {}", false); debug_assert_eq!("a".len(), 1, "tadam {}", 1); debug_assert_eq!("a".len(), 1, "tadam {}", true); debug_assert_eq!("a".is_empty(), false, "tadam {}", 1); debug_assert_eq!("a".is_empty(), false, "tadam {}", true); debug_assert_eq!(false, "a".is_empty(), "tadam {}", true); + debug_assert_eq!(a, true, "tadam {}", false); } diff --git a/tests/ui/bool_assert_comparison.stderr b/tests/ui/bool_assert_comparison.stderr index f57acf520d5f1..da9b56aa7795d 100644 --- a/tests/ui/bool_assert_comparison.stderr +++ b/tests/ui/bool_assert_comparison.stderr @@ -1,5 +1,5 @@ error: used `assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:16:5 + --> $DIR/bool_assert_comparison.rs:69:5 | LL | assert_eq!("a".is_empty(), false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` @@ -7,106 +7,130 @@ LL | assert_eq!("a".is_empty(), false); = note: `-D clippy::bool-assert-comparison` implied by `-D warnings` error: used `assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:17:5 + --> $DIR/bool_assert_comparison.rs:70:5 | LL | assert_eq!("".is_empty(), true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` error: used `assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:18:5 + --> $DIR/bool_assert_comparison.rs:71:5 | LL | assert_eq!(true, "".is_empty()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` +error: used `assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:76:5 + | +LL | assert_eq!(b, true); + | ^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + error: used `assert_ne!` with a literal bool - --> $DIR/bool_assert_comparison.rs:24:5 + --> $DIR/bool_assert_comparison.rs:79:5 | LL | assert_ne!("a".is_empty(), false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` error: used `assert_ne!` with a literal bool - --> $DIR/bool_assert_comparison.rs:25:5 + --> $DIR/bool_assert_comparison.rs:80:5 | LL | assert_ne!("".is_empty(), true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` error: used `assert_ne!` with a literal bool - --> $DIR/bool_assert_comparison.rs:26:5 + --> $DIR/bool_assert_comparison.rs:81:5 | LL | assert_ne!(true, "".is_empty()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` +error: used `assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:86:5 + | +LL | assert_ne!(b, true); + | ^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + error: used `debug_assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:32:5 + --> $DIR/bool_assert_comparison.rs:89:5 | LL | debug_assert_eq!("a".is_empty(), false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` error: used `debug_assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:33:5 + --> $DIR/bool_assert_comparison.rs:90:5 | LL | debug_assert_eq!("".is_empty(), true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` error: used `debug_assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:34:5 + --> $DIR/bool_assert_comparison.rs:91:5 | LL | debug_assert_eq!(true, "".is_empty()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` +error: used `debug_assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:96:5 + | +LL | debug_assert_eq!(b, true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + error: used `debug_assert_ne!` with a literal bool - --> $DIR/bool_assert_comparison.rs:40:5 + --> $DIR/bool_assert_comparison.rs:99:5 | LL | debug_assert_ne!("a".is_empty(), false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` error: used `debug_assert_ne!` with a literal bool - --> $DIR/bool_assert_comparison.rs:41:5 + --> $DIR/bool_assert_comparison.rs:100:5 | LL | debug_assert_ne!("".is_empty(), true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` error: used `debug_assert_ne!` with a literal bool - --> $DIR/bool_assert_comparison.rs:42:5 + --> $DIR/bool_assert_comparison.rs:101:5 | LL | debug_assert_ne!(true, "".is_empty()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` +error: used `debug_assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:106:5 + | +LL | debug_assert_ne!(b, true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + error: used `assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:50:5 + --> $DIR/bool_assert_comparison.rs:111:5 | LL | assert_eq!("a".is_empty(), false, "tadam {}", 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` error: used `assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:51:5 + --> $DIR/bool_assert_comparison.rs:112:5 | LL | assert_eq!("a".is_empty(), false, "tadam {}", true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` error: used `assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:52:5 + --> $DIR/bool_assert_comparison.rs:113:5 | LL | assert_eq!(false, "a".is_empty(), "tadam {}", true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` error: used `debug_assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:56:5 + --> $DIR/bool_assert_comparison.rs:118:5 | LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` error: used `debug_assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:57:5 + --> $DIR/bool_assert_comparison.rs:119:5 | LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` error: used `debug_assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:58:5 + --> $DIR/bool_assert_comparison.rs:120:5 | LL | debug_assert_eq!(false, "a".is_empty(), "tadam {}", true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` -error: aborting due to 18 previous errors +error: aborting due to 22 previous errors From aee4f1fc0cae5ac2c044e4f1b6ff015bbb9405b4 Mon Sep 17 00:00:00 2001 From: xordi Date: Thu, 26 Aug 2021 18:18:17 +0200 Subject: [PATCH 58/94] Fix CI errors --- clippy_lints/src/bool_assert_comparison.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/bool_assert_comparison.rs b/clippy_lints/src/bool_assert_comparison.rs index 171d22f998666..2f8f61f7d33ab 100644 --- a/clippy_lints/src/bool_assert_comparison.rs +++ b/clippy_lints/src/bool_assert_comparison.rs @@ -1,7 +1,7 @@ use clippy_utils::{diagnostics::span_lint_and_sugg, higher, is_direct_expn_of, ty::implements_trait}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::*; +use rustc_hir::{Expr, ExprKind, Lit}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; From 83f1454ade1bfa9a797b4bdccd8bd2432c110641 Mon Sep 17 00:00:00 2001 From: xordi Date: Tue, 31 Aug 2021 09:06:14 +0200 Subject: [PATCH 59/94] Fix function and variable names --- clippy_lints/src/bool_assert_comparison.rs | 16 +++++++-------- tests/ui/bool_assert_comparison.rs | 24 +++++++++++----------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/clippy_lints/src/bool_assert_comparison.rs b/clippy_lints/src/bool_assert_comparison.rs index 2f8f61f7d33ab..cdc192a47e48a 100644 --- a/clippy_lints/src/bool_assert_comparison.rs +++ b/clippy_lints/src/bool_assert_comparison.rs @@ -40,23 +40,23 @@ fn is_bool_lit(e: &Expr<'_>) -> bool { ) && !e.span.from_expansion() } -fn impl_not_trait_with_bool_out(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { +fn is_impl_not_trait_with_bool_out(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(e); cx.tcx .lang_items() .not_trait() - .filter(|id| implements_trait(cx, ty, *id, &[])) - .and_then(|id| { - cx.tcx.associated_items(id).find_by_name_and_kind( + .filter(|trait_id| implements_trait(cx, ty, *trait_id, &[])) + .and_then(|trait_id| { + cx.tcx.associated_items(trait_id).find_by_name_and_kind( cx.tcx, Ident::from_str("Output"), ty::AssocKind::Type, - id, + trait_id, ) }) - .map_or(false, |item| { - let proj = cx.tcx.mk_projection(item.def_id, cx.tcx.mk_substs_trait(ty, &[])); + .map_or(false, |assoc_item| { + let proj = cx.tcx.mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(ty, &[])); let nty = cx.tcx.normalize_erasing_regions(cx.param_env, proj); nty.is_bool() @@ -82,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison { return; } - if !impl_not_trait_with_bool_out(cx, a) || !impl_not_trait_with_bool_out(cx, b) { + if !is_impl_not_trait_with_bool_out(cx, a) || !is_impl_not_trait_with_bool_out(cx, b) { // At this point the expression which is not a boolean // literal does not implement Not trait with a bool output, // so we cannot suggest to rewrite our code diff --git a/tests/ui/bool_assert_comparison.rs b/tests/ui/bool_assert_comparison.rs index 3961b9432a302..ec4d6f3ff8401 100644 --- a/tests/ui/bool_assert_comparison.rs +++ b/tests/ui/bool_assert_comparison.rs @@ -16,28 +16,28 @@ macro_rules! b { // Implements the Not trait but with an output type // that's not bool. Should not suggest a rewrite #[derive(Debug)] -enum A { +enum ImplNotTraitWithoutBool { VariantX(bool), VariantY(u32), } -impl PartialEq for A { +impl PartialEq for ImplNotTraitWithoutBool { fn eq(&self, other: &bool) -> bool { match *self { - A::VariantX(b) => b == *other, + ImplNotTraitWithoutBool::VariantX(b) => b == *other, _ => false, } } } -impl Not for A { +impl Not for ImplNotTraitWithoutBool { type Output = Self; fn not(self) -> Self::Output { match self { - A::VariantX(b) => A::VariantX(!b), - A::VariantY(0) => A::VariantY(1), - A::VariantY(_) => A::VariantY(0), + ImplNotTraitWithoutBool::VariantX(b) => ImplNotTraitWithoutBool::VariantX(!b), + ImplNotTraitWithoutBool::VariantY(0) => ImplNotTraitWithoutBool::VariantY(1), + ImplNotTraitWithoutBool::VariantY(_) => ImplNotTraitWithoutBool::VariantY(0), } } } @@ -45,15 +45,15 @@ impl Not for A { // This type implements the Not trait with an Output of // type bool. Using assert!(..) must be suggested #[derive(Debug)] -struct B; +struct ImplNotTraitWithBool; -impl PartialEq for B { +impl PartialEq for ImplNotTraitWithBool { fn eq(&self, other: &bool) -> bool { false } } -impl Not for B { +impl Not for ImplNotTraitWithBool { type Output = bool; fn not(self) -> Self::Output { @@ -62,8 +62,8 @@ impl Not for B { } fn main() { - let a = A::VariantX(true); - let b = B {}; + let a = ImplNotTraitWithoutBool::VariantX(true); + let b = ImplNotTraitWithBool; assert_eq!("a".len(), 1); assert_eq!("a".is_empty(), false); From 0fa9bf4a4034be0f8df64d3bb3ec2687a9c33031 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Wed, 1 Sep 2021 21:55:08 +0200 Subject: [PATCH 60/94] Reference new diagnostic item docs in our docs :) --- doc/common_tools_writing_lints.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/common_tools_writing_lints.md b/doc/common_tools_writing_lints.md index 5f45951090e7e..1a6b7c8cb47a9 100644 --- a/doc/common_tools_writing_lints.md +++ b/doc/common_tools_writing_lints.md @@ -11,6 +11,7 @@ You may need following tooltips to catch up with common operations. Useful Rustc dev guide links: - [Stages of compilation](https://rustc-dev-guide.rust-lang.org/compiler-src.html#the-main-stages-of-compilation) +- [Diagnostic items](https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html) - [Type checking](https://rustc-dev-guide.rust-lang.org/type-checking.html) - [Ty module](https://rustc-dev-guide.rust-lang.org/ty.html) From 6a9549c358369304f487a3d7a50b4c8275247e61 Mon Sep 17 00:00:00 2001 From: Aaron <79121360+UnrealApex@users.noreply.github.com> Date: Wed, 1 Sep 2021 14:04:24 -0700 Subject: [PATCH 61/94] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e1c968273cdf6..aaf404eadea11 100644 --- a/README.md +++ b/README.md @@ -45,13 +45,13 @@ or in Travis CI. One way to use Clippy is by installing Clippy through rustup as a cargo subcommand. -#### Step 1: Install rustup +#### Step 1: Install Rustup -You can install [rustup](https://rustup.rs/) on supported platforms. This will help +You can install [Rustup](https://rustup.rs/) on supported platforms. This will help us install Clippy and its dependencies. -If you already have rustup installed, update to ensure you have the latest -rustup and compiler: +If you already have Rustup installed, update to ensure you have the latest +Rustup and compiler: ```terminal rustup update From 8f7c249980d1e09e1ac7adbcfeda1fd1e4edc081 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Mon, 30 Aug 2021 17:38:27 +0300 Subject: [PATCH 62/94] rustc_target: move `LayoutOf` to `ty::layout`. --- clippy_lints/src/casts/cast_ptr_alignment.rs | 2 +- clippy_lints/src/escape.rs | 5 +++-- clippy_lints/src/invalid_upcast_comparisons.rs | 2 +- clippy_lints/src/large_const_arrays.rs | 2 +- clippy_lints/src/large_enum_variant.rs | 2 +- clippy_lints/src/large_stack_arrays.rs | 3 +-- clippy_lints/src/methods/manual_saturating_arithmetic.rs | 2 +- clippy_lints/src/pass_by_ref_or_value.rs | 2 +- clippy_lints/src/types/vec_box.rs | 2 +- clippy_lints/src/vec.rs | 2 +- clippy_lints/src/zero_sized_map_values.rs | 2 +- 11 files changed, 13 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/casts/cast_ptr_alignment.rs b/clippy_lints/src/casts/cast_ptr_alignment.rs index 62a119d662bb2..5dcf1824ef0b7 100644 --- a/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -3,9 +3,9 @@ use clippy_utils::is_hir_ty_cfg_dependant; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, GenericArg}; use rustc_lint::LateContext; +use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Ty}; use rustc_span::symbol::sym; -use rustc_target::abi::LayoutOf; use super::CAST_PTR_ALIGNMENT; diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 685dbf26250ce..1ba241d377616 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -5,11 +5,11 @@ use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKi use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::FakeReadCause; +use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, TraitRef, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::kw; -use rustc_target::abi::LayoutOf; use rustc_target::spec::abi::Abi; use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; @@ -171,7 +171,8 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { // skip if there is a `self` parameter binding to a type // that contains `Self` (i.e.: `self: Box`), see #4804 if let Some(trait_self_ty) = self.trait_self_ty { - if map.name(cmt.hir_id) == kw::SelfLower && contains_ty(self.cx.tcx, cmt.place.ty(), trait_self_ty) { + if map.name(cmt.hir_id) == kw::SelfLower && contains_ty(self.cx.tcx, cmt.place.ty(), trait_self_ty) + { return; } } diff --git a/clippy_lints/src/invalid_upcast_comparisons.rs b/clippy_lints/src/invalid_upcast_comparisons.rs index 3b28b1212048a..b1f70b30c12cf 100644 --- a/clippy_lints/src/invalid_upcast_comparisons.rs +++ b/clippy_lints/src/invalid_upcast_comparisons.rs @@ -2,10 +2,10 @@ use std::cmp::Ordering; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, IntTy, UintTy}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; -use rustc_target::abi::LayoutOf; use clippy_utils::comparisons::Rel; use clippy_utils::consts::{constant, Constant}; diff --git a/clippy_lints/src/large_const_arrays.rs b/clippy_lints/src/large_const_arrays.rs index 5d4e06c2af082..10281496c11cb 100644 --- a/clippy_lints/src/large_const_arrays.rs +++ b/clippy_lints/src/large_const_arrays.rs @@ -1,10 +1,10 @@ -use crate::rustc_target::abi::LayoutOf; use clippy_utils::diagnostics::span_lint_and_then; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::interpret::ConstValue; +use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, ConstKind}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{BytePos, Pos, Span}; diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index cde2336b690fd..e4b8e7546283b 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -6,8 +6,8 @@ use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind, VariantData}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::layout::LayoutOf; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_target::abi::LayoutOf; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/large_stack_arrays.rs b/clippy_lints/src/large_stack_arrays.rs index 7088630bfdbb4..bbb6c1f902ce0 100644 --- a/clippy_lints/src/large_stack_arrays.rs +++ b/clippy_lints/src/large_stack_arrays.rs @@ -4,11 +4,10 @@ use if_chain::if_chain; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::interpret::ConstValue; +use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, ConstKind}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use crate::rustc_target::abi::LayoutOf; - declare_clippy_lint! { /// ### What it does /// Checks for local arrays that may be too large. diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index 2fddea7068d96..7ecafa1f3ba5b 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -6,7 +6,7 @@ use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_target::abi::LayoutOf; +use rustc_middle::ty::layout::LayoutOf; pub fn check( cx: &LateContext<'_>, diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 157b18c1f6b1f..6229b9608b3cb 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -13,10 +13,10 @@ use rustc_hir::intravisit::FnKind; use rustc_hir::{BindingAnnotation, Body, FnDecl, HirId, Impl, ItemKind, MutTy, Mutability, Node, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; +use rustc_middle::ty::layout::LayoutOf; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::def_id::LocalDefId; use rustc_span::{sym, Span}; -use rustc_target::abi::LayoutOf; use rustc_target::spec::abi::Abi; use rustc_target::spec::Target; diff --git a/clippy_lints/src/types/vec_box.rs b/clippy_lints/src/types/vec_box.rs index 7a444174626f4..e7e2016d8f2f3 100644 --- a/clippy_lints/src/types/vec_box.rs +++ b/clippy_lints/src/types/vec_box.rs @@ -5,9 +5,9 @@ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{self as hir, def_id::DefId, GenericArg, QPath, TyKind}; use rustc_lint::LateContext; +use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::TypeFoldable; use rustc_span::symbol::sym; -use rustc_target::abi::LayoutOf; use rustc_typeck::hir_ty_to_ty; use super::VEC_BOX; diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index 95a45fa937f11..e76d5f81c9640 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -1,4 +1,3 @@ -use crate::rustc_target::abi::LayoutOf; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher; @@ -8,6 +7,7 @@ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; diff --git a/clippy_lints/src/zero_sized_map_values.rs b/clippy_lints/src/zero_sized_map_values.rs index 2fbe27f94798b..8e1cd655b6111 100644 --- a/clippy_lints/src/zero_sized_map_values.rs +++ b/clippy_lints/src/zero_sized_map_values.rs @@ -3,10 +3,10 @@ use clippy_utils::ty::{is_normalizable, is_type_diagnostic_item}; use if_chain::if_chain; use rustc_hir::{self as hir, HirId, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::layout::LayoutOf as _; use rustc_middle::ty::{Adt, Ty, TypeFoldable}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; -use rustc_target::abi::LayoutOf as _; use rustc_typeck::hir_ty_to_ty; declare_clippy_lint! { From 5722a7dacdfa84d125adaa872dfe2ec0a34e8200 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 26 Aug 2021 10:05:33 -0500 Subject: [PATCH 63/94] Fix manual_match with let-expressions --- clippy_lints/src/collapsible_match.rs | 32 +-- clippy_lints/src/manual_map.rs | 334 ++++++++++++-------------- clippy_utils/src/higher.rs | 29 ++- clippy_utils/src/lib.rs | 7 + 4 files changed, 191 insertions(+), 211 deletions(-) diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/collapsible_match.rs index f5683856889f2..86226be715d3f 100644 --- a/clippy_lints/src/collapsible_match.rs +++ b/clippy_lints/src/collapsible_match.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::higher::IfLetOrMatch; use clippy_utils::visitors::is_local_used; -use clippy_utils::{higher, is_lang_ctor, is_unit_expr, path_to_local, peel_ref_operators, SpanlessEq}; +use clippy_utils::{is_lang_ctor, is_unit_expr, path_to_local, peel_ref_operators, SpanlessEq}; use if_chain::if_chain; use rustc_hir::LangItem::OptionNone; -use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, MatchSource, Pat, PatKind, StmtKind}; +use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{MultiSpan, Span}; @@ -149,33 +150,6 @@ fn strip_singleton_blocks<'hir>(mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> expr } -enum IfLetOrMatch<'hir> { - Match(&'hir Expr<'hir>, &'hir [Arm<'hir>], MatchSource), - /// scrutinee, pattern, then block, else block - IfLet( - &'hir Expr<'hir>, - &'hir Pat<'hir>, - &'hir Expr<'hir>, - Option<&'hir Expr<'hir>>, - ), -} - -impl<'hir> IfLetOrMatch<'hir> { - fn parse(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option { - match expr.kind { - ExprKind::Match(expr, arms, source) => Some(Self::Match(expr, arms, source)), - _ => higher::IfLet::hir(cx, expr).map( - |higher::IfLet { - let_expr, - let_pat, - if_then, - if_else, - }| { Self::IfLet(let_expr, let_pat, if_then, if_else) }, - ), - } - } -} - /// A "wild-like" arm has a wild (`_`) or `None` pattern and no guard. Such arms can be "collapsed" /// into a single wild arm without any significant loss in semantics or readability. fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index 385a3f546b9d0..b5f573cb104e9 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -1,6 +1,6 @@ use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::higher; +use clippy_utils::higher::IfLetOrMatch; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; use clippy_utils::{ @@ -46,190 +46,169 @@ declare_lint_pass!(ManualMap => [MANUAL_MAP]); impl LateLintPass<'_> for ManualMap { #[allow(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let Some(higher::IfLet { - let_pat, - let_expr, - if_then, - if_else: Some(if_else), - }) = higher::IfLet::hir(cx, expr) - { - manage_lint(cx, expr, (&let_pat.kind, if_then), (&PatKind::Wild, if_else), let_expr); + let (scrutinee, then_pat, then_body, else_pat, else_body) = match IfLetOrMatch::parse(cx, expr) { + Some(IfLetOrMatch::IfLet(scrutinee, pat, body, Some(r#else))) => (scrutinee, pat, body, None, r#else), + Some(IfLetOrMatch::Match( + scrutinee, + [arm1 @ Arm { guard: None, .. }, arm2 @ Arm { guard: None, .. }], + _, + )) => (scrutinee, arm1.pat, arm1.body, Some(arm2.pat), arm2.body), + _ => return, + }; + if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) { + return; } - if let ExprKind::Match(scrutinee, [then @ Arm { guard: None, .. }, r#else @ Arm { guard: None, .. }], _) = - expr.kind + let (scrutinee_ty, ty_ref_count, ty_mutability) = + peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee)); + if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::option_type) + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::option_type)) { - manage_lint( - cx, - expr, - (&then.pat.kind, then.body), - (&r#else.pat.kind, r#else.body), - scrutinee, - ); + return; } - } -} - -fn manage_lint<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, - then: (&'tcx PatKind<'_>, &'tcx Expr<'_>), - r#else: (&'tcx PatKind<'_>, &'tcx Expr<'_>), - scrut: &'tcx Expr<'_>, -) { - if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) { - return; - } - - let (scrutinee_ty, ty_ref_count, ty_mutability) = peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrut)); - if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::option_type) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::option_type)) - { - return; - } - - let (then_pat, then_expr) = then; - let (else_pat, else_expr) = r#else; - let expr_ctxt = expr.span.ctxt(); - let (some_expr, some_pat, pat_ref_count, is_wild_none) = match ( - try_parse_pattern(cx, then_pat, expr_ctxt), - try_parse_pattern(cx, else_pat, expr_ctxt), - ) { - (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_expr) => { - (else_expr, pattern, ref_count, true) - }, - (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_expr) => { - (else_expr, pattern, ref_count, false) - }, - (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_expr) => { - (then_expr, pattern, ref_count, true) - }, - (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_expr) => { - (then_expr, pattern, ref_count, false) - }, - _ => return, - }; + let expr_ctxt = expr.span.ctxt(); + let (some_expr, some_pat, pat_ref_count, is_wild_none) = match ( + try_parse_pattern(cx, then_pat, expr_ctxt), + else_pat.map_or(Some(OptionPat::Wild), |p| try_parse_pattern(cx, p, expr_ctxt)), + ) { + (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { + (else_body, pattern, ref_count, true) + }, + (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { + (else_body, pattern, ref_count, false) + }, + (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_body) => { + (then_body, pattern, ref_count, true) + }, + (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_body) => { + (then_body, pattern, ref_count, false) + }, + _ => return, + }; - // Top level or patterns aren't allowed in closures. - if matches!(some_pat.kind, PatKind::Or(_)) { - return; - } + // Top level or patterns aren't allowed in closures. + if matches!(some_pat.kind, PatKind::Or(_)) { + return; + } - let some_expr = match get_some_expr(cx, some_expr, expr_ctxt) { - Some(expr) => expr, - None => return, - }; + let some_expr = match get_some_expr(cx, some_expr, expr_ctxt) { + Some(expr) => expr, + None => return, + }; - if cx.typeck_results().expr_ty(some_expr) == cx.tcx.types.unit - && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id) - { - return; - } + // These two lints will go back and forth with each other. + if cx.typeck_results().expr_ty(some_expr) == cx.tcx.types.unit + && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id) + { + return; + } - // `map` won't perform any adjustments. - if !cx.typeck_results().expr_adjustments(some_expr).is_empty() { - return; - } + // `map` won't perform any adjustments. + if !cx.typeck_results().expr_adjustments(some_expr).is_empty() { + return; + } - // Determine which binding mode to use. - let explicit_ref = some_pat.contains_explicit_ref_binding(); - let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then(|| ty_mutability)); + // Determine which binding mode to use. + let explicit_ref = some_pat.contains_explicit_ref_binding(); + let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then(|| ty_mutability)); - let as_ref_str = match binding_ref { - Some(Mutability::Mut) => ".as_mut()", - Some(Mutability::Not) => ".as_ref()", - None => "", - }; + let as_ref_str = match binding_ref { + Some(Mutability::Mut) => ".as_mut()", + Some(Mutability::Not) => ".as_ref()", + None => "", + }; - match can_move_expr_to_closure(cx, some_expr) { - Some(captures) => { - // Check if captures the closure will need conflict with borrows made in the scrutinee. - // TODO: check all the references made in the scrutinee expression. This will require interacting - // with the borrow checker. Currently only `[.]*` is checked for. - if let Some(binding_ref_mutability) = binding_ref { - let e = peel_hir_expr_while(scrut, |e| match e.kind { - ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e), - _ => None, - }); - if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind { - match captures.get(l) { - Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return, - Some(CaptureKind::Ref(Mutability::Not)) if binding_ref_mutability == Mutability::Mut => { - return; - }, - Some(CaptureKind::Ref(Mutability::Not)) | None => (), + match can_move_expr_to_closure(cx, some_expr) { + Some(captures) => { + // Check if captures the closure will need conflict with borrows made in the scrutinee. + // TODO: check all the references made in the scrutinee expression. This will require interacting + // with the borrow checker. Currently only `[.]*` is checked for. + if let Some(binding_ref_mutability) = binding_ref { + let e = peel_hir_expr_while(scrutinee, |e| match e.kind { + ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e), + _ => None, + }); + if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind { + match captures.get(l) { + Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return, + Some(CaptureKind::Ref(Mutability::Not)) if binding_ref_mutability == Mutability::Mut => { + return; + }, + Some(CaptureKind::Ref(Mutability::Not)) | None => (), + } } } - } - }, - None => return, - }; - - let mut app = Applicability::MachineApplicable; + }, + None => return, + }; - // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or - // it's being passed by value. - let scrutinee = peel_hir_expr_refs(scrut).0; - let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); - let scrutinee_str = if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX { - format!("({})", scrutinee_str) - } else { - scrutinee_str.into() - }; + let mut app = Applicability::MachineApplicable; - let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind { - match can_pass_as_func(cx, id, some_expr) { - Some(func) if func.span.ctxt() == some_expr.span.ctxt() => { - snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() - }, - _ => { - if path_to_local_id(some_expr, id) - && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id) - && binding_ref.is_some() - { - return; - } + // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or + // it's being passed by value. + let scrutinee = peel_hir_expr_refs(scrutinee).0; + let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); + let scrutinee_str = + if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX { + format!("({})", scrutinee_str) + } else { + scrutinee_str.into() + }; - // `ref` and `ref mut` annotations were handled earlier. - let annotation = if matches!(annotation, BindingAnnotation::Mutable) { - "mut " - } else { - "" - }; - format!( - "|{}{}| {}", - annotation, - some_binding, - snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0 - ) - }, - } - } else if !is_wild_none && explicit_ref.is_none() { - // TODO: handle explicit reference annotations. - format!( - "|{}| {}", - snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0, - snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0 - ) - } else { - // Refutable bindings and mixed reference annotations can't be handled by `map`. - return; - }; + let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind { + match can_pass_as_func(cx, id, some_expr) { + Some(func) if func.span.ctxt() == some_expr.span.ctxt() => { + snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() + }, + _ => { + if path_to_local_id(some_expr, id) + && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id) + && binding_ref.is_some() + { + return; + } - span_lint_and_sugg( - cx, - MANUAL_MAP, - expr.span, - "manual implementation of `Option::map`", - "try this", - if is_else_clause(cx.tcx, expr) { - format!("{{ {}{}.map({}) }}", scrutinee_str, as_ref_str, body_str) + // `ref` and `ref mut` annotations were handled earlier. + let annotation = if matches!(annotation, BindingAnnotation::Mutable) { + "mut " + } else { + "" + }; + format!( + "|{}{}| {}", + annotation, + some_binding, + snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0 + ) + }, + } + } else if !is_wild_none && explicit_ref.is_none() { + // TODO: handle explicit reference annotations. + format!( + "|{}| {}", + snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0, + snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0 + ) } else { - format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str) - }, - app, - ); + // Refutable bindings and mixed reference annotations can't be handled by `map`. + return; + }; + + span_lint_and_sugg( + cx, + MANUAL_MAP, + expr.span, + "manual implementation of `Option::map`", + "try this", + if else_pat.is_none() && is_else_clause(cx.tcx, expr) { + format!("{{ {}{}.map({}) }}", scrutinee_str, as_ref_str, body_str) + } else { + format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str) + }, + app, + ); + } } // Checks whether the expression could be passed as a function, or whether a closure is needed. @@ -259,28 +238,21 @@ enum OptionPat<'a> { // Try to parse into a recognized `Option` pattern. // i.e. `_`, `None`, `Some(..)`, or a reference to any of those. -fn try_parse_pattern( - cx: &LateContext<'tcx>, - pat_kind: &'tcx PatKind<'_>, - ctxt: SyntaxContext, -) -> Option> { - fn f( - cx: &LateContext<'tcx>, - pat_kind: &'tcx PatKind<'_>, - ref_count: usize, - ctxt: SyntaxContext, - ) -> Option> { - match pat_kind { +fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option> { + fn f(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ref_count: usize, ctxt: SyntaxContext) -> Option> { + match pat.kind { PatKind::Wild => Some(OptionPat::Wild), - PatKind::Ref(ref_pat, _) => f(cx, &ref_pat.kind, ref_count + 1, ctxt), + PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone) => Some(OptionPat::None), - PatKind::TupleStruct(ref qpath, [pattern], _) if is_lang_ctor(cx, qpath, OptionSome) => { + PatKind::TupleStruct(ref qpath, [pattern], _) + if is_lang_ctor(cx, qpath, OptionSome) && pat.span.ctxt() == ctxt => + { Some(OptionPat::Some { pattern, ref_count }) }, _ => None, } } - f(cx, pat_kind, 0, ctxt) + f(cx, pat, 0, ctxt) } // Checks for an expression wrapped by the `Some` constructor. Returns the contained expression. diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index d20739a9f74cc..61e672781f840 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -6,7 +6,7 @@ use crate::{is_expn_of, match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast::{self, LitKind}; use rustc_hir as hir; -use rustc_hir::{Block, BorrowKind, Expr, ExprKind, LoopSource, Node, Pat, StmtKind, UnOp}; +use rustc_hir::{Arm, Block, BorrowKind, Expr, ExprKind, LoopSource, MatchSource, Node, Pat, StmtKind, UnOp}; use rustc_lint::LateContext; use rustc_span::{sym, ExpnKind, Span, Symbol}; @@ -115,6 +115,33 @@ impl<'hir> IfLet<'hir> { } } +pub enum IfLetOrMatch<'hir> { + Match(&'hir Expr<'hir>, &'hir [Arm<'hir>], MatchSource), + /// scrutinee, pattern, then block, else block + IfLet( + &'hir Expr<'hir>, + &'hir Pat<'hir>, + &'hir Expr<'hir>, + Option<&'hir Expr<'hir>>, + ), +} + +impl<'hir> IfLetOrMatch<'hir> { + pub fn parse(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option { + match expr.kind { + ExprKind::Match(expr, arms, source) => Some(Self::Match(expr, arms, source)), + _ => IfLet::hir(cx, expr).map( + |IfLet { + let_expr, + let_pat, + if_then, + if_else, + }| { Self::IfLet(let_expr, let_pat, if_then, if_else) }, + ), + } + } +} + pub struct IfOrIfLet<'hir> { pub cond: &'hir Expr<'hir>, pub r#else: Option<&'hir Expr<'hir>>, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 7e03cf56e8a86..6cecdd6b19963 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -808,6 +808,13 @@ pub fn capture_local_usage(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind capture_expr_ty = e; } }, + ExprKind::Let(pat, ..) => { + let mutability = match pat_capture_kind(cx, pat) { + CaptureKind::Value => Mutability::Not, + CaptureKind::Ref(m) => m, + }; + return CaptureKind::Ref(mutability); + }, ExprKind::Match(_, arms, _) => { let mut mutability = Mutability::Not; for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) { From 588367b62ec0e6c30cb8d8783644775742da91fc Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 26 Aug 2021 10:25:14 -0500 Subject: [PATCH 64/94] Fix dogfood --- clippy_lints/src/assertions_on_constants.rs | 2 +- clippy_lints/src/floating_point_arithmetic.rs | 2 -- clippy_lints/src/functions/must_use.rs | 1 - clippy_lints/src/if_let_some_result.rs | 4 ++-- clippy_lints/src/loops/mod.rs | 2 +- clippy_lints/src/loops/while_let_loop.rs | 6 ++--- clippy_lints/src/matches.rs | 14 +++++------ clippy_lints/src/pattern_type_mismatch.rs | 2 +- clippy_lints/src/question_mark.rs | 2 +- clippy_lints/src/ranges.rs | 2 +- clippy_lints/src/redundant_closure_call.rs | 4 +--- clippy_lints/src/returns.rs | 11 ++++----- clippy_lints/src/unused_async.rs | 7 +----- clippy_lints/src/utils/inspector.rs | 2 +- clippy_lints/src/vec.rs | 2 +- clippy_utils/src/higher.rs | 24 +++++++++---------- clippy_utils/src/hir_utils.rs | 6 ++--- clippy_utils/src/lib.rs | 4 ++-- 18 files changed, 41 insertions(+), 56 deletions(-) diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index 891e865b245dd..d834a1d317a0f 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -118,7 +118,7 @@ enum AssertKind { fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option { if_chain! { if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr); - if let ExprKind::Unary(UnOp::Not, ref expr) = cond.kind; + if let ExprKind::Unary(UnOp::Not, expr) = cond.kind; // bind the first argument of the `assert!` macro if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), expr); // block diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index d12482e7b7bb9..a643b3eb8f1b8 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -332,8 +332,6 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { ), Applicability::MachineApplicable, ); - - return; } } } diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index ea6193acbe845..77d08081c07f1 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -26,7 +26,6 @@ pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); if let Some(attr) = attr { check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr); - return; } else if is_public && !is_proc_macro(cx.sess(), attrs) && !attrs.iter().any(|a| a.has_name(sym::no_mangle)) { check_must_use_candidate( cx, diff --git a/clippy_lints/src/if_let_some_result.rs b/clippy_lints/src/if_let_some_result.rs index fb5637fcec183..33eba02dae063 100644 --- a/clippy_lints/src/if_let_some_result.rs +++ b/clippy_lints/src/if_let_some_result.rs @@ -46,8 +46,8 @@ impl<'tcx> LateLintPass<'tcx> for OkIfLet { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { //begin checking variables if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(cx, expr); - if let ExprKind::MethodCall(_, ok_span, ref result_types, _) = let_expr.kind; //check is expr.ok() has type Result.ok(, _) - if let PatKind::TupleStruct(QPath::Resolved(_, ref x), ref y, _) = let_pat.kind; //get operation + if let ExprKind::MethodCall(_, ok_span, result_types, _) = 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 is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&result_types[0]), sym::result_type); if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some"; diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index bd9de5e08d736..9c224445b1592 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -580,7 +580,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops { while_let_on_iterator::check(cx, expr); - if let Some(higher::While { if_cond, if_then, .. }) = higher::While::hir(&expr) { + if let Some(higher::While { if_cond, if_then, .. }) = higher::While::hir(expr) { while_immutable_condition::check(cx, if_cond, if_then); } diff --git a/clippy_lints/src/loops/while_let_loop.rs b/clippy_lints/src/loops/while_let_loop.rs index d6d3315e0a83e..1848f5b5de2f2 100644 --- a/clippy_lints/src/loops/while_let_loop.rs +++ b/clippy_lints/src/loops/while_let_loop.rs @@ -24,13 +24,13 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &' } } - if let ExprKind::Match(ref matchexpr, ref arms, MatchSource::Normal) = inner.kind { + if let ExprKind::Match(matchexpr, arms, MatchSource::Normal) = inner.kind { if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() - && is_simple_break_expr(&arms[1].body) + && is_simple_break_expr(arms[1].body) { - could_be_while_let(cx, expr, &arms[0].pat, matchexpr); + could_be_while_let(cx, expr, arms[0].pat, matchexpr); } } } diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 131ed1d8925f0..2f1ff567e844f 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -631,7 +631,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { check_match_single_binding(cx, ex, arms, expr); } } - if let ExprKind::Match(ref ex, ref arms, _) = expr.kind { + if let ExprKind::Match(ex, arms, _) = expr.kind { check_match_ref_pats(cx, ex, arms.iter().map(|el| el.pat), expr); } if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(cx, expr) { @@ -1194,7 +1194,7 @@ where let (first_sugg, msg, title); let span = ex.span.source_callsite(); - if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = ex.kind { + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = ex.kind { first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string())); msg = "try"; title = "you don't need to add `&` to both the expression and the patterns"; @@ -1205,7 +1205,7 @@ where } let remaining_suggs = pats.filter_map(|pat| { - if let PatKind::Ref(ref refp, _) = pat.kind { + if let PatKind::Ref(refp, _) = pat.kind { Some((pat.span, snippet(cx, refp.span, "..").to_string())) } else { None @@ -1365,7 +1365,7 @@ where find_bool_lit(&arm.2.kind, is_if_let).map_or(false, |b| b == b0) && arm.3.is_none() && arm.0.is_empty() }); then { - if let Some(ref last_pat) = last_pat_opt { + if let Some(last_pat) = last_pat_opt { if !is_wild(last_pat) { return false; } @@ -1827,13 +1827,13 @@ mod redundant_pattern_match { .. }) = higher::IfLet::hir(cx, expr) { - find_sugg_for_if_let(cx, expr, let_pat, let_expr, "if", if_else.is_some()) + find_sugg_for_if_let(cx, expr, let_pat, let_expr, "if", if_else.is_some()); } if let ExprKind::Match(op, arms, MatchSource::Normal) = &expr.kind { - find_sugg_for_match(cx, expr, op, arms) + find_sugg_for_match(cx, expr, op, arms); } if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) { - find_sugg_for_if_let(cx, expr, let_pat, let_expr, "while", false) + find_sugg_for_if_let(cx, expr, let_pat, let_expr, "while", false); } } diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index 35cff4141a903..e7bc24465908b 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -118,7 +118,7 @@ impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch { } } if let ExprKind::Let(let_pat, let_expr, _) = expr.kind { - if let Some(ref expr_ty) = cx.typeck_results().node_type_opt(let_expr.hir_id) { + if let Some(expr_ty) = cx.typeck_results().node_type_opt(let_expr.hir_id) { if in_external_macro(cx.sess(), let_pat.span) { return; } diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 17c9e3e7541ca..e79cd7ed4ec4a 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -106,7 +106,7 @@ impl QuestionMark { if let PatKind::Binding(annot, bind_id, _, _) = fields[0].kind; let by_ref = matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut); - if let ExprKind::Block(ref block, None) = if_then.kind; + if let ExprKind::Block(block, None) = if_then.kind; if block.stmts.is_empty(); if let Some(trailing_expr) = &block.expr; if path_to_local_id(trailing_expr, bind_id); diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 4fa361fedafac..87364a88ed0dc 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -337,7 +337,7 @@ fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: // `.iter()` and `.len()` called on same `Path` if let ExprKind::Path(QPath::Resolved(_, iter_path)) = iter_args[0].kind; if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_args[0].kind; - if SpanlessEq::new(cx).eq_path_segments(&iter_path.segments, &len_path.segments); + if SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments); then { span_lint(cx, RANGE_ZIP_WITH_LEN, diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 7314bce83e038..90e3c3f4b3e98 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -51,9 +51,7 @@ impl ReturnVisitor { impl<'ast> ast_visit::Visitor<'ast> for ReturnVisitor { fn visit_expr(&mut self, ex: &'ast ast::Expr) { - if let ast::ExprKind::Ret(_) = ex.kind { - self.found_return = true; - } else if let ast::ExprKind::Try(_) = ex.kind { + if let ast::ExprKind::Ret(_) | ast::ExprKind::Try(_) = ex.kind { self.found_return = true; } diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 681baed8c3696..341b5a61631df 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -206,13 +206,10 @@ fn check_final_expr<'tcx>( // an if/if let expr, check both exprs // note, if without else is going to be a type checking error anyways // (except for unit type functions) so we don't match it - ExprKind::Match(_, arms, source) => match source { - MatchSource::Normal => { - for arm in arms.iter() { - check_final_expr(cx, arm.body, Some(arm.body.span), RetReplacement::Block); - } - }, - _ => (), + ExprKind::Match(_, arms, MatchSource::Normal) => { + for arm in arms.iter() { + check_final_expr(cx, arm.body, Some(arm.body.span), RetReplacement::Block); + } }, ExprKind::DropTemps(expr) => check_final_expr(cx, expr, None, RetReplacement::Empty), _ => (), diff --git a/clippy_lints/src/unused_async.rs b/clippy_lints/src/unused_async.rs index 3a6a07c522630..f4808682b6927 100644 --- a/clippy_lints/src/unused_async.rs +++ b/clippy_lints/src/unused_async.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, NestedVisitorMap, Visitor}; -use rustc_hir::{Body, Expr, ExprKind, FnDecl, FnHeader, HirId, IsAsync, Item, ItemKind, YieldSource}; +use rustc_hir::{Body, Expr, ExprKind, FnDecl, FnHeader, HirId, IsAsync, YieldSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -57,11 +57,6 @@ impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> { } impl<'tcx> LateLintPass<'tcx> for UnusedAsync { - fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if let ItemKind::Trait(..) = item.kind { - return; - } - } fn check_fn( &mut self, cx: &LateContext<'tcx>, diff --git a/clippy_lints/src/utils/inspector.rs b/clippy_lints/src/utils/inspector.rs index e97983a2e1451..43590cc786236 100644 --- a/clippy_lints/src/utils/inspector.rs +++ b/clippy_lints/src/utils/inspector.rs @@ -142,7 +142,7 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { print_expr(cx, arg, indent + 1); } }, - hir::ExprKind::Let(ref pat, ref expr, _) => { + hir::ExprKind::Let(pat, expr, _) => { print_pat(cx, pat, indent + 1); print_expr(cx, expr, indent + 1); }, diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index 95a45fa937f11..85ff292c53a4d 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -49,7 +49,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { if_chain! { if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(expr).kind(); if let ty::Slice(..) = ty.kind(); - if let ExprKind::AddrOf(BorrowKind::Ref, mutability, ref addressee) = expr.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, mutability, addressee) = expr.kind; if let Some(vec_args) = higher::VecArgs::hir(cx, addressee); then { self.check_vec_macro(cx, &vec_args, mutability, expr.span); diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 61e672781f840..4bb31868bafb9 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -23,16 +23,16 @@ impl<'tcx> ForLoop<'tcx> { #[inline] pub fn hir(expr: &Expr<'tcx>) -> Option { if_chain! { - if let hir::ExprKind::Match(ref iterexpr, ref arms, hir::MatchSource::ForLoopDesugar) = expr.kind; + if let hir::ExprKind::Match(iterexpr, arms, hir::MatchSource::ForLoopDesugar) = expr.kind; if let Some(first_arm) = arms.get(0); - if let hir::ExprKind::Call(_, ref iterargs) = iterexpr.kind; + if let hir::ExprKind::Call(_, iterargs) = iterexpr.kind; if let Some(first_arg) = iterargs.get(0); if iterargs.len() == 1 && arms.len() == 1 && first_arm.guard.is_none(); - if let hir::ExprKind::Loop(ref block, ..) = first_arm.body.kind; + if let hir::ExprKind::Loop(block, ..) = first_arm.body.kind; if block.expr.is_none(); if let [ _, _, ref let_stmt, ref body ] = *block.stmts; - if let hir::StmtKind::Local(ref local) = let_stmt.kind; - if let hir::StmtKind::Expr(ref body_expr) = body.kind; + if let hir::StmtKind::Local(local) = let_stmt.kind; + if let hir::StmtKind::Expr(body_expr) = body.kind; then { return Some(Self { pat: &*local.pat, @@ -189,7 +189,7 @@ impl<'a> Range<'a> { } match expr.kind { - hir::ExprKind::Call(ref path, ref args) + hir::ExprKind::Call(path, args) if matches!( path.kind, hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, _)) @@ -201,7 +201,7 @@ impl<'a> Range<'a> { limits: ast::RangeLimits::Closed, }) }, - hir::ExprKind::Struct(ref path, ref fields, None) => match path { + hir::ExprKind::Struct(path, fields, None) => match &path { hir::QPath::LangItem(hir::LangItem::RangeFull, _) => Some(Range { start: None, end: None, @@ -247,7 +247,7 @@ impl<'a> VecArgs<'a> { /// from `vec!`. pub fn hir(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> Option> { if_chain! { - if let hir::ExprKind::Call(ref fun, ref args) = expr.kind; + if let hir::ExprKind::Call(fun, args) = expr.kind; if let hir::ExprKind::Path(ref qpath) = fun.kind; if is_expn_of(fun.span, "vec").is_some(); if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); @@ -259,10 +259,10 @@ impl<'a> VecArgs<'a> { else if match_def_path(cx, fun_def_id, &paths::SLICE_INTO_VEC) && args.len() == 1 { // `vec![a, b, c]` case if_chain! { - if let hir::ExprKind::Box(ref boxed) = args[0].kind; - if let hir::ExprKind::Array(ref args) = boxed.kind; + if let hir::ExprKind::Box(boxed) = args[0].kind; + if let hir::ExprKind::Array(args) = boxed.kind; then { - return Some(VecArgs::Vec(&*args)); + return Some(VecArgs::Vec(args)); } } @@ -566,7 +566,7 @@ pub fn is_from_for_desugar(local: &hir::Local<'_>) -> bool { // } // ``` if_chain! { - if let Some(ref expr) = local.init; + if let Some(expr) = local.init; if let hir::ExprKind::Match(_, _, hir::MatchSource::ForLoopDesugar) = expr.kind; then { return true; diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index a44f2df2fd631..6e9a1de21eef5 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -232,9 +232,7 @@ impl HirEqInterExpr<'_, '_, '_> { (&ExprKind::If(lc, lt, ref le), &ExprKind::If(rc, rt, ref re)) => { self.eq_expr(lc, rc) && self.eq_expr(&**lt, &**rt) && both(le, re, |l, r| self.eq_expr(l, r)) }, - (&ExprKind::Let(ref lp, ref le, _), &ExprKind::Let(ref rp, ref re, _)) => { - self.eq_pat(lp, rp) && self.eq_expr(le, re) - }, + (&ExprKind::Let(lp, le, _), &ExprKind::Let(rp, re, _)) => self.eq_pat(lp, rp) && self.eq_expr(le, re), (&ExprKind::Lit(ref l), &ExprKind::Lit(ref r)) => l.node == r.node, (&ExprKind::Loop(lb, ref ll, ref lls, _), &ExprKind::Loop(rb, ref rl, ref rls, _)) => { lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.name == r.ident.name) @@ -668,7 +666,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } } }, - ExprKind::Let(ref pat, ref expr, _) => { + ExprKind::Let(pat, expr, _) => { self.hash_expr(expr); self.hash_pat(pat); }, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 6cecdd6b19963..40d7963a5e545 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1603,13 +1603,13 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) { conds.push(&*cond); - if let ExprKind::Block(ref block, _) = then.kind { + if let ExprKind::Block(block, _) = then.kind { blocks.push(block); } else { panic!("ExprKind::If node is not an ExprKind::Block"); } - if let Some(ref else_expr) = r#else { + if let Some(else_expr) = r#else { expr = else_expr; } else { break; From c7c2036cb92dae475d7d6ef6b03919fead5bf052 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 26 Aug 2021 16:53:07 +0100 Subject: [PATCH 65/94] Fix remaining dogfood errors Except for the missing docs ones --- CHANGELOG.md | 4 ++-- clippy_utils/src/qualify_min_const_fn.rs | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5708837034371..d86414a87f4d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -964,7 +964,7 @@ Released 2020-11-19 [#5907](https://github.com/rust-lang/rust-clippy/pull/5907) * [`suspicious_arithmetic_impl`]: extend to implementations of `BitAnd`, `BitOr`, `BitXor`, `Rem`, `Shl`, and `Shr` [#5884](https://github.com/rust-lang/rust-clippy/pull/5884) -* [`invalid_atomic_ordering`]: detect misuse of `compare_exchange`, `compare_exchange_weak`, and `fetch_update` +* `invalid_atomic_ordering`: detect misuse of `compare_exchange`, `compare_exchange_weak`, and `fetch_update` [#6025](https://github.com/rust-lang/rust-clippy/pull/6025) * Avoid [`redundant_pattern_matching`] triggering in macros [#6069](https://github.com/rust-lang/rust-clippy/pull/6069) @@ -1451,7 +1451,7 @@ Released 2020-03-12 * [`option_as_ref_deref`] [#4945](https://github.com/rust-lang/rust-clippy/pull/4945) * [`wildcard_in_or_patterns`] [#4960](https://github.com/rust-lang/rust-clippy/pull/4960) * [`iter_nth_zero`] [#4966](https://github.com/rust-lang/rust-clippy/pull/4966) -* [`invalid_atomic_ordering`] [#4999](https://github.com/rust-lang/rust-clippy/pull/4999) +* `invalid_atomic_ordering` [#4999](https://github.com/rust-lang/rust-clippy/pull/4999) * [`skip_while_next`] [#5067](https://github.com/rust-lang/rust-clippy/pull/5067) ### Moves and Deprecations diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index e5bbf75c3b0a1..6cb2bd7f6efba 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -36,9 +36,7 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&Ru ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate), ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate), ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {:#?}", predicate), - ty::PredicateKind::Coerce(_) => { - panic!("coerce predicate on function: {:#?}", predicate) - }, + ty::PredicateKind::Coerce(_) => panic!("coerce predicate on function: {:#?}", predicate), ty::PredicateKind::Trait(pred) => { if Some(pred.def_id()) == tcx.lang_items().sized_trait() { continue; From c2bb313e7aeb19c8e7f0eab490adb957ec7aa0cc Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 27 Aug 2021 08:38:07 -0500 Subject: [PATCH 66/94] Add higher docs and remove some unneeded fields --- clippy_lints/src/loops/mod.rs | 4 +- .../src/loops/while_let_on_iterator.rs | 7 +- clippy_utils/src/higher.rs | 103 +++++++++++------- 3 files changed, 65 insertions(+), 49 deletions(-) diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 9c224445b1592..97cbe2c53ddac 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -580,8 +580,8 @@ impl<'tcx> LateLintPass<'tcx> for Loops { while_let_on_iterator::check(cx, expr); - if let Some(higher::While { if_cond, if_then, .. }) = higher::While::hir(expr) { - while_immutable_condition::check(cx, if_cond, if_then); + if let Some(higher::While { condition, body }) = higher::While::hir(expr) { + while_immutable_condition::check(cx, condition, body); } needless_collect::check(expr, cx); diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index 0757d329125cb..79527e3bfa92f 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -14,12 +14,7 @@ use rustc_span::{symbol::sym, Span, Symbol}; pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let (scrutinee_expr, iter_expr, some_pat, loop_expr) = if_chain! { - if let Some(higher::WhileLet { - if_then, - let_pat, - let_expr, - .. - }) = higher::WhileLet::hir(expr); + if let Some(higher::WhileLet { if_then, let_pat, let_expr }) = higher::WhileLet::hir(expr); // check for `Some(..)` pattern if let PatKind::TupleStruct(QPath::Resolved(None, pat_path), some_pat, _) = let_pat.kind; if let Res::Def(_, pat_did) = pat_path.res; diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 4bb31868bafb9..05a4a01431950 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -13,14 +13,19 @@ use rustc_span::{sym, ExpnKind, Span, Symbol}; /// The essential nodes of a desugared for loop as well as the entire span: /// `for pat in arg { body }` becomes `(pat, arg, body)`. Return `(pat, arg, body, span)`. pub struct ForLoop<'tcx> { + /// `for` loop item pub pat: &'tcx hir::Pat<'tcx>, + /// `IntoIterator` argument pub arg: &'tcx hir::Expr<'tcx>, + /// `for` loop body pub body: &'tcx hir::Expr<'tcx>, + /// entire `for` loop span pub span: Span, } impl<'tcx> ForLoop<'tcx> { #[inline] + /// Parses a desugared `for` loop pub fn hir(expr: &Expr<'tcx>) -> Option { if_chain! { if let hir::ExprKind::Match(iterexpr, arms, hir::MatchSource::ForLoopDesugar) = expr.kind; @@ -46,14 +51,19 @@ impl<'tcx> ForLoop<'tcx> { } } +/// An `if` expression without `DropTemps` pub struct If<'hir> { + /// `if` condition pub cond: &'hir Expr<'hir>, - pub r#else: Option<&'hir Expr<'hir>>, + /// `if` then expression pub then: &'hir Expr<'hir>, + /// `else` expression + pub r#else: Option<&'hir Expr<'hir>>, } impl<'hir> If<'hir> { #[inline] + /// Parses an `if` expression pub const fn hir(expr: &Expr<'hir>) -> Option { if let ExprKind::If( Expr { @@ -64,21 +74,27 @@ impl<'hir> If<'hir> { r#else, ) = expr.kind { - Some(Self { cond, r#else, then }) + Some(Self { cond, then, r#else }) } else { None } } } +/// An `if let` expression pub struct IfLet<'hir> { + /// `if let` pattern pub let_pat: &'hir Pat<'hir>, + /// `if let` scrutinee pub let_expr: &'hir Expr<'hir>, + /// `if let` then expression pub if_then: &'hir Expr<'hir>, + /// `if let` else expression pub if_else: Option<&'hir Expr<'hir>>, } impl<'hir> IfLet<'hir> { + /// Parses an `if let` expression pub fn hir(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option { if let ExprKind::If( Expr { @@ -115,7 +131,9 @@ impl<'hir> IfLet<'hir> { } } +/// An `if let` or `match` expression. Useful for lints that trigger on one or the other. pub enum IfLetOrMatch<'hir> { + /// Any `match` expression Match(&'hir Expr<'hir>, &'hir [Arm<'hir>], MatchSource), /// scrutinee, pattern, then block, else block IfLet( @@ -127,6 +145,7 @@ pub enum IfLetOrMatch<'hir> { } impl<'hir> IfLetOrMatch<'hir> { + /// Parses an `if let` or `match` expression pub fn parse(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option { match expr.kind { ExprKind::Match(expr, arms, source) => Some(Self::Match(expr, arms, source)), @@ -142,14 +161,19 @@ impl<'hir> IfLetOrMatch<'hir> { } } +/// An `if` or `if let` expression pub struct IfOrIfLet<'hir> { + /// `if` condition that is maybe a `let` expression pub cond: &'hir Expr<'hir>, - pub r#else: Option<&'hir Expr<'hir>>, + /// `if` then expression pub then: &'hir Expr<'hir>, + /// `else` expression + pub r#else: Option<&'hir Expr<'hir>>, } impl<'hir> IfOrIfLet<'hir> { #[inline] + /// Parses an `if` or `if let` expression pub const fn hir(expr: &Expr<'hir>) -> Option { if let ExprKind::If(cond, then, r#else) = expr.kind { if let ExprKind::DropTemps(new_cond) = cond.kind { @@ -160,7 +184,7 @@ impl<'hir> IfOrIfLet<'hir> { }); } if let ExprKind::Let(..) = cond.kind { - return Some(Self { cond, r#else, then }); + return Some(Self { cond, then, r#else }); } } None @@ -281,14 +305,17 @@ impl<'a> VecArgs<'a> { } } +/// A desugared `while` loop pub struct While<'hir> { - pub if_cond: &'hir Expr<'hir>, - pub if_then: &'hir Expr<'hir>, - pub if_else: Option<&'hir Expr<'hir>>, + /// `while` loop condition + pub condition: &'hir Expr<'hir>, + /// `while` loop body + pub body: &'hir Expr<'hir>, } impl<'hir> While<'hir> { #[inline] + /// Parses a desugared `while` loop pub const fn hir(expr: &Expr<'hir>) -> Option { if let ExprKind::Loop( Block { @@ -297,11 +324,11 @@ impl<'hir> While<'hir> { kind: ExprKind::If( Expr { - kind: ExprKind::DropTemps(if_cond), + kind: ExprKind::DropTemps(condition), .. }, - if_then, - if_else_ref, + body, + _, ), .. }), @@ -312,59 +339,53 @@ impl<'hir> While<'hir> { _, ) = expr.kind { - let if_else = *if_else_ref; - return Some(Self { - if_cond, - if_then, - if_else, - }); + return Some(Self { condition, body }); } None } } +/// A desugared `while let` loop pub struct WhileLet<'hir> { - pub if_expr: &'hir Expr<'hir>, + /// `while let` loop item pattern pub let_pat: &'hir Pat<'hir>, + /// `while let` loop scrutinee pub let_expr: &'hir Expr<'hir>, + /// `while let` loop body pub if_then: &'hir Expr<'hir>, - pub if_else: Option<&'hir Expr<'hir>>, } impl<'hir> WhileLet<'hir> { #[inline] + /// Parses a desugared `while let` loop pub const fn hir(expr: &Expr<'hir>) -> Option { if let ExprKind::Loop( Block { - expr: Some(if_expr), .. + expr: + Some(Expr { + kind: + ExprKind::If( + Expr { + kind: ExprKind::Let(let_pat, let_expr, _), + .. + }, + if_then, + _, + ), + .. + }), + .. }, _, LoopSource::While, _, ) = expr.kind { - if let Expr { - kind: - ExprKind::If( - Expr { - kind: ExprKind::Let(let_pat, let_expr, _), - .. - }, - if_then, - if_else_ref, - ), - .. - } = if_expr - { - let if_else = *if_else_ref; - return Some(Self { - if_expr, - let_pat, - let_expr, - if_then, - if_else, - }); - } + return Some(Self { + let_pat, + let_expr, + if_then, + }); } None } From 8bf2940abb478139e575cba302a4ea100c4652cb Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 28 Aug 2021 15:04:28 +0200 Subject: [PATCH 67/94] Fix matadata collection configuration formatting --- clippy_lints/src/utils/internal_lints/metadata_collector.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 91533695eb309..188d0419c3993 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -786,8 +786,6 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityResolver<'a, 'hir> { } }; - // TODO xFrednet 2021-03-01: support function arguments? - intravisit::walk_expr(self, expr); } } From fb6839db2d4a6e0239c400a599e691c7ec969ad1 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 2 Sep 2021 12:45:45 +0100 Subject: [PATCH 68/94] Bump nightly version -> 2021-09-02 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 23887f1784549..8b0563eda6d1d 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-08-12" +channel = "nightly-2021-09-02" components = ["llvm-tools-preview", "rustc-dev", "rust-src"] From 01b17af1083ffc0630255c7ac172d32812c610fa Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 2 Sep 2021 12:58:25 +0100 Subject: [PATCH 69/94] Fix fallout from re-applying patches --- clippy_lints/src/collapsible_match.rs | 3 ++- clippy_lints/src/lib.rs | 1 + clippy_lints/src/option_if_let_else.rs | 4 +++- clippy_lints/src/utils/internal_lints.rs | 24 ++++++++---------------- clippy_utils/src/sugg.rs | 2 +- 5 files changed, 15 insertions(+), 19 deletions(-) diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/collapsible_match.rs index 86226be715d3f..a4693fa213bc4 100644 --- a/clippy_lints/src/collapsible_match.rs +++ b/clippy_lints/src/collapsible_match.rs @@ -107,7 +107,8 @@ 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)); // ...or anywhere in the inner expression + if outer_guard.map_or(true, |(Guard::If(e) | Guard::IfLet(_, e))| !is_local_used(cx, *e, binding_id)); + // ...or anywhere in the inner expression if match inner { IfLetOrMatch::IfLet(_, _, body, els) => { !is_local_used(cx, body, binding_id) && els.map_or(true, |e| !is_local_used(cx, e, binding_id)) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 5ce5f809f4692..2b76eacb7d61c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -2199,6 +2199,7 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { ls.register_renamed("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"); ls.register_renamed("clippy::panic_params", "non_fmt_panics"); ls.register_renamed("clippy::unknown_clippy_lints", "unknown_lints"); + ls.register_renamed("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"); } // only exists to let the dogfood integration test works. diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 80b4f544b1a46..15f6dcae8870b 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -3,7 +3,8 @@ use clippy_utils::higher; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{ - can_move_expr_to_closure, eager_or_lazy, in_macro, is_else_clause, is_lang_ctor, peel_hir_expr_while, CaptureKind, + can_move_expr_to_closure, eager_or_lazy, in_constant, in_macro, is_else_clause, is_lang_ctor, peel_hir_expr_while, + CaptureKind, }; use if_chain::if_chain; use rustc_errors::Applicability; @@ -126,6 +127,7 @@ fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: boo fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option { if_chain! { if !in_macro(expr.span); // Don't lint macros, because it behaves weirdly + if !in_constant(cx, expr.hir_id); if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) }) = higher::IfLet::hir(cx, expr); if !is_else_clause(cx.tcx, expr); diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index d660008e7d184..42d51272279e2 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,5 +1,6 @@ use clippy_utils::consts::{constant_simple, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::higher; use clippy_utils::source::snippet; use clippy_utils::ty::match_type; use clippy_utils::{ @@ -17,8 +18,8 @@ use rustc_hir::def_id::DefId; use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; use rustc_hir::{ - BinOpKind, Block, Crate, Expr, ExprKind, HirId, Item, Local, MatchSource, MutTy, Mutability, Node, Path, Stmt, - StmtKind, Ty, TyKind, UnOp, + BinOpKind, Block, Crate, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, Ty, + TyKind, UnOp, }; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; @@ -1106,16 +1107,10 @@ impl<'tcx> LateLintPass<'tcx> for IfChainStyle { } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - let (cond, then, els) = match expr.kind { - ExprKind::If(cond, then, els) => (Some(cond), then, els.is_some()), - ExprKind::Match( - _, - [arm, ..], - MatchSource::IfLetDesugar { - contains_else_clause: els, - }, - ) => (None, arm.body, els), - _ => return, + let (cond, then, els) = if let Some(higher::IfOrIfLet { cond, r#else, then }) = higher::IfOrIfLet::hir(expr) { + (cond, then, r#else.is_some()) + } else { + return; }; let then_block = match then.kind { ExprKind::Block(block, _) => block, @@ -1131,7 +1126,6 @@ impl<'tcx> LateLintPass<'tcx> for IfChainStyle { }; // check for `if a && b;` if_chain! { - if let Some(cond) = cond; if let ExprKind::Binary(op, _, _) = cond.kind; if op.node == BinOpKind::And; if cx.sess().source_map().is_multiline(cond.span); @@ -1166,9 +1160,7 @@ fn check_nested_if_chains( _ => return, }; if_chain! { - if matches!(tail.kind, - ExprKind::If(_, _, None) - | ExprKind::Match(.., MatchSource::IfLetDesugar { contains_else_clause: false })); + if let Some(higher::IfOrIfLet { r#else: None, .. }) = higher::IfOrIfLet::hir(tail); let sm = cx.sess().source_map(); if head .iter() diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 65d93e8f86e43..ab05a0b423853 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -329,7 +329,7 @@ fn has_enclosing_paren(sugg: impl AsRef) -> bool { } } -// Copied from the rust standart library, and then edited +/// Copied from the rust standard library, and then edited macro_rules! forward_binop_impls_to_ref { (impl $imp:ident, $method:ident for $t:ty, type Output = $o:ty) => { impl $imp<$t> for &$t { From 03d3131cb46ad8139466a58759ac312f9ebaf053 Mon Sep 17 00:00:00 2001 From: Jake Goulding Date: Wed, 18 Aug 2021 09:56:25 -0400 Subject: [PATCH 70/94] Extend unnecessary_unwrap to look for expect in addition to unwrap Closes #7581 --- clippy_lints/src/unwrap.rs | 4 +- .../ui/checked_unwrap/simple_conditionals.rs | 4 ++ .../checked_unwrap/simple_conditionals.stderr | 62 +++++++++++++++---- 3 files changed, 55 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index bffd9f3612b0a..42738bc3e1fbb 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -172,8 +172,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { if_chain! { if let ExprKind::MethodCall(method_name, _, args, _) = expr.kind; if let ExprKind::Path(QPath::Resolved(None, path)) = args[0].kind; - if [sym::unwrap, sym!(unwrap_err)].contains(&method_name.ident.name); - let call_to_unwrap = method_name.ident.name == sym::unwrap; + if [sym::unwrap, sym::expect, sym!(unwrap_err)].contains(&method_name.ident.name); + let call_to_unwrap = [sym::unwrap, sym::expect].contains(&method_name.ident.name); if let Some(unwrappable) = self.unwrappables.iter() .find(|u| u.ident.res == path.res); // Span contexts should not differ with the conditional branch diff --git a/tests/ui/checked_unwrap/simple_conditionals.rs b/tests/ui/checked_unwrap/simple_conditionals.rs index 8f23fb28827a2..ee3fdfabe9d8a 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/tests/ui/checked_unwrap/simple_conditionals.rs @@ -37,8 +37,10 @@ fn main() { let x = Some(()); if x.is_some() { x.unwrap(); // unnecessary + x.expect("an error message"); // unnecessary } else { x.unwrap(); // will panic + x.expect("an error message"); // will panic } if x.is_none() { x.unwrap(); // will panic @@ -52,9 +54,11 @@ fn main() { let mut x: Result<(), ()> = Ok(()); if x.is_ok() { x.unwrap(); // unnecessary + x.expect("an error message"); // unnecessary x.unwrap_err(); // will panic } else { x.unwrap(); // will panic + x.expect("an error message"); // will panic x.unwrap_err(); // unnecessary } if x.is_err() { diff --git a/tests/ui/checked_unwrap/simple_conditionals.stderr b/tests/ui/checked_unwrap/simple_conditionals.stderr index a4bc058fe2020..7127ff89b31db 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.stderr +++ b/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -12,8 +12,17 @@ note: the lint level is defined here LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: you checked before that `expect()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` + --> $DIR/simple_conditionals.rs:40:9 + | +LL | if x.is_some() { + | ----------- the check is happening here +LL | x.unwrap(); // unnecessary +LL | x.expect("an error message"); // unnecessary + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: this call to `unwrap()` will always panic - --> $DIR/simple_conditionals.rs:41:9 + --> $DIR/simple_conditionals.rs:42:9 | LL | if x.is_some() { | ----------- because of this check @@ -27,8 +36,17 @@ note: the lint level is defined here LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] | ^^^^^^^^^^^^^^^^^^^^^^^^ +error: this call to `expect()` will always panic + --> $DIR/simple_conditionals.rs:43:9 + | +LL | if x.is_some() { + | ----------- because of this check +... +LL | x.expect("an error message"); // will panic + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: this call to `unwrap()` will always panic - --> $DIR/simple_conditionals.rs:44:9 + --> $DIR/simple_conditionals.rs:46:9 | LL | if x.is_none() { | ----------- because of this check @@ -36,7 +54,7 @@ LL | x.unwrap(); // will panic | ^^^^^^^^^^ error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` - --> $DIR/simple_conditionals.rs:46:9 + --> $DIR/simple_conditionals.rs:48:9 | LL | if x.is_none() { | ----------- the check is happening here @@ -58,24 +76,33 @@ LL | m!(x); = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` - --> $DIR/simple_conditionals.rs:54:9 + --> $DIR/simple_conditionals.rs:56:9 | LL | if x.is_ok() { | --------- the check is happening here LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ +error: you checked before that `expect()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` + --> $DIR/simple_conditionals.rs:57:9 + | +LL | if x.is_ok() { + | --------- the check is happening here +LL | x.unwrap(); // unnecessary +LL | x.expect("an error message"); // unnecessary + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: this call to `unwrap_err()` will always panic - --> $DIR/simple_conditionals.rs:55:9 + --> $DIR/simple_conditionals.rs:58:9 | LL | if x.is_ok() { | --------- because of this check -LL | x.unwrap(); // unnecessary +... LL | x.unwrap_err(); // will panic | ^^^^^^^^^^^^^^ error: this call to `unwrap()` will always panic - --> $DIR/simple_conditionals.rs:57:9 + --> $DIR/simple_conditionals.rs:60:9 | LL | if x.is_ok() { | --------- because of this check @@ -83,8 +110,17 @@ LL | if x.is_ok() { LL | x.unwrap(); // will panic | ^^^^^^^^^^ +error: this call to `expect()` will always panic + --> $DIR/simple_conditionals.rs:61:9 + | +LL | if x.is_ok() { + | --------- because of this check +... +LL | x.expect("an error message"); // will panic + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` - --> $DIR/simple_conditionals.rs:58:9 + --> $DIR/simple_conditionals.rs:62:9 | LL | if x.is_ok() { | --------- the check is happening here @@ -93,7 +129,7 @@ LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ error: this call to `unwrap()` will always panic - --> $DIR/simple_conditionals.rs:61:9 + --> $DIR/simple_conditionals.rs:65:9 | LL | if x.is_err() { | ---------- because of this check @@ -101,7 +137,7 @@ LL | x.unwrap(); // will panic | ^^^^^^^^^^ error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` - --> $DIR/simple_conditionals.rs:62:9 + --> $DIR/simple_conditionals.rs:66:9 | LL | if x.is_err() { | ---------- the check is happening here @@ -110,7 +146,7 @@ LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` - --> $DIR/simple_conditionals.rs:64:9 + --> $DIR/simple_conditionals.rs:68:9 | LL | if x.is_err() { | ---------- the check is happening here @@ -119,7 +155,7 @@ LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ error: this call to `unwrap_err()` will always panic - --> $DIR/simple_conditionals.rs:65:9 + --> $DIR/simple_conditionals.rs:69:9 | LL | if x.is_err() { | ---------- because of this check @@ -127,5 +163,5 @@ LL | if x.is_err() { LL | x.unwrap_err(); // will panic | ^^^^^^^^^^^^^^ -error: aborting due to 13 previous errors +error: aborting due to 17 previous errors From b4775435e875e3668ad920d911fabaed58f8860f Mon Sep 17 00:00:00 2001 From: Jake Goulding Date: Wed, 18 Aug 2021 16:33:50 -0400 Subject: [PATCH 71/94] Adjust the output of unnecessary_unwrap and provide a suggestion --- clippy_lints/src/unwrap.rs | 129 +++++++++++++++--- clippy_utils/src/usage.rs | 11 +- .../complex_conditionals.stderr | 39 ++++-- .../complex_conditionals_nested.stderr | 4 +- .../checked_unwrap/simple_conditionals.stderr | 36 ++--- 5 files changed, 161 insertions(+), 58 deletions(-) diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 42738bc3e1fbb..b2ab300c2e937 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -1,10 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{differing_macro_contexts, usage::is_potentially_mutated}; +use clippy_utils::{differing_macro_contexts, path_to_local, usage::is_potentially_mutated}; use if_chain::if_chain; +use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, NestedVisitorMap, Visitor}; -use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Path, QPath, UnOp}; +use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, PathSegment, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; @@ -74,26 +75,61 @@ struct UnwrappableVariablesVisitor<'a, 'tcx> { unwrappables: Vec>, cx: &'a LateContext<'tcx>, } + +/// What kind of unwrappable this is. +#[derive(Copy, Clone, Debug)] +enum UnwrappableKind { + Option, + Result, +} + +impl UnwrappableKind { + fn success_variant_pattern(self) -> &'static str { + match self { + UnwrappableKind::Option => "Some(..)", + UnwrappableKind::Result => "Ok(..)", + } + } + + fn error_variant_pattern(self) -> &'static str { + match self { + UnwrappableKind::Option => "None", + UnwrappableKind::Result => "Err(..)", + } + } +} + /// Contains information about whether a variable can be unwrapped. #[derive(Copy, Clone, Debug)] struct UnwrapInfo<'tcx> { /// The variable that is checked - ident: &'tcx Path<'tcx>, + local_id: HirId, + /// The if itself + if_expr: &'tcx Expr<'tcx>, /// The check, like `x.is_ok()` check: &'tcx Expr<'tcx>, + /// The check's name, like `is_ok` + check_name: &'tcx PathSegment<'tcx>, /// The branch where the check takes place, like `if x.is_ok() { .. }` branch: &'tcx Expr<'tcx>, /// Whether `is_some()` or `is_ok()` was called (as opposed to `is_err()` or `is_none()`). safe_to_unwrap: bool, + /// What kind of unwrappable this is. + kind: UnwrappableKind, + /// If the check is the entire condition (`if x.is_ok()`) or only a part of it (`foo() && + /// x.is_ok()`) + is_entire_condition: bool, } /// Collects the information about unwrappable variables from an if condition /// The `invert` argument tells us whether the condition is negated. fn collect_unwrap_info<'tcx>( cx: &LateContext<'tcx>, + if_expr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>, branch: &'tcx Expr<'_>, invert: bool, + is_entire_condition: bool, ) -> Vec> { fn is_relevant_option_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: &str) -> bool { is_type_diagnostic_item(cx, ty, sym::option_type) && ["is_some", "is_none"].contains(&method_name) @@ -106,18 +142,18 @@ fn collect_unwrap_info<'tcx>( if let ExprKind::Binary(op, left, right) = &expr.kind { match (invert, op.node) { (false, BinOpKind::And | BinOpKind::BitAnd) | (true, BinOpKind::Or | BinOpKind::BitOr) => { - let mut unwrap_info = collect_unwrap_info(cx, left, branch, invert); - unwrap_info.append(&mut collect_unwrap_info(cx, right, branch, invert)); + let mut unwrap_info = collect_unwrap_info(cx, if_expr, left, branch, invert, false); + unwrap_info.append(&mut collect_unwrap_info(cx, if_expr, right, branch, invert, false)); return unwrap_info; }, _ => (), } } else if let ExprKind::Unary(UnOp::Not, expr) = &expr.kind { - return collect_unwrap_info(cx, expr, branch, !invert); + return collect_unwrap_info(cx, if_expr, expr, branch, !invert, false); } else { if_chain! { if let ExprKind::MethodCall(method_name, _, args, _) = &expr.kind; - if let ExprKind::Path(QPath::Resolved(None, path)) = &args[0].kind; + if let Some(local_id) = path_to_local(&args[0]); let ty = cx.typeck_results().expr_ty(&args[0]); let name = method_name.ident.as_str(); if is_relevant_option_call(cx, ty, &name) || is_relevant_result_call(cx, ty, &name); @@ -129,7 +165,24 @@ fn collect_unwrap_info<'tcx>( _ => unreachable!(), }; let safe_to_unwrap = unwrappable != invert; - return vec![UnwrapInfo { ident: path, check: expr, branch, safe_to_unwrap }]; + let kind = if is_type_diagnostic_item(cx, ty, sym::option_type) { + UnwrappableKind::Option + } else { + UnwrappableKind::Result + }; + + return vec![ + UnwrapInfo { + local_id, + if_expr, + check: expr, + check_name: method_name, + branch, + safe_to_unwrap, + kind, + is_entire_condition, + } + ] } } } @@ -137,11 +190,17 @@ fn collect_unwrap_info<'tcx>( } impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> { - fn visit_branch(&mut self, cond: &'tcx Expr<'_>, branch: &'tcx Expr<'_>, else_branch: bool) { + fn visit_branch( + &mut self, + if_expr: &'tcx Expr<'_>, + cond: &'tcx Expr<'_>, + branch: &'tcx Expr<'_>, + else_branch: bool, + ) { let prev_len = self.unwrappables.len(); - for unwrap_info in collect_unwrap_info(self.cx, cond, branch, else_branch) { - if is_potentially_mutated(unwrap_info.ident, cond, self.cx) - || is_potentially_mutated(unwrap_info.ident, branch, self.cx) + for unwrap_info in collect_unwrap_info(self.cx, if_expr, cond, branch, else_branch, true) { + if is_potentially_mutated(unwrap_info.local_id, cond, self.cx) + || is_potentially_mutated(unwrap_info.local_id, branch, self.cx) { // if the variable is mutated, we don't know whether it can be unwrapped: continue; @@ -163,32 +222,62 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { } if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr) { walk_expr(self, cond); - self.visit_branch(cond, then, false); + self.visit_branch(expr, cond, then, false); if let Some(else_inner) = r#else { - self.visit_branch(cond, else_inner, true); + self.visit_branch(expr, cond, else_inner, true); } } else { // find `unwrap[_err]()` calls: if_chain! { if let ExprKind::MethodCall(method_name, _, args, _) = expr.kind; - if let ExprKind::Path(QPath::Resolved(None, path)) = args[0].kind; + if let Some(id) = path_to_local(&args[0]); if [sym::unwrap, sym::expect, sym!(unwrap_err)].contains(&method_name.ident.name); let call_to_unwrap = [sym::unwrap, sym::expect].contains(&method_name.ident.name); if let Some(unwrappable) = self.unwrappables.iter() - .find(|u| u.ident.res == path.res); + .find(|u| u.local_id == id); // Span contexts should not differ with the conditional branch if !differing_macro_contexts(unwrappable.branch.span, expr.span); if !differing_macro_contexts(unwrappable.branch.span, unwrappable.check.span); then { if call_to_unwrap == unwrappable.safe_to_unwrap { + let is_entire_condition = unwrappable.is_entire_condition; + let unwrappable_variable_name = self.cx.tcx.hir().name(unwrappable.local_id); + let suggested_pattern = if call_to_unwrap { + unwrappable.kind.success_variant_pattern() + } else { + unwrappable.kind.error_variant_pattern() + }; + span_lint_and_then( self.cx, UNNECESSARY_UNWRAP, expr.span, - &format!("you checked before that `{}()` cannot fail, \ - instead of checking and unwrapping, it's better to use `if let` or `match`", - method_name.ident.name), - |diag| { diag.span_label(unwrappable.check.span, "the check is happening here"); }, + &format!( + "called `{}` on `{}` after checking its variant with `{}`", + method_name.ident.name, + unwrappable_variable_name, + unwrappable.check_name.ident.as_str(), + ), + |diag| { + if is_entire_condition { + diag.span_suggestion( + unwrappable.check.span.with_lo(unwrappable.if_expr.span.lo()), + "try", + format!( + "if let {} = {}", + suggested_pattern, + unwrappable_variable_name, + ), + // We don't track how the unwrapped value is used inside the + // block or suggest deleting the unwrap, so we can't offer a + // fixable solution. + Applicability::Unspecified, + ); + } else { + diag.span_label(unwrappable.check.span, "the check is happening here"); + diag.help("try using `if let` or `match`"); + } + }, ); } else { span_lint_and_then( diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index ac885e9994404..098ec175fe2d9 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -1,10 +1,9 @@ use crate as utils; use rustc_hir as hir; -use rustc_hir::def::Res; use rustc_hir::intravisit; use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; use rustc_hir::HirIdSet; -use rustc_hir::{Expr, ExprKind, HirId, Path}; +use rustc_hir::{Expr, ExprKind, HirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; @@ -35,12 +34,8 @@ pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> Some(delegate.used_mutably) } -pub fn is_potentially_mutated<'tcx>(variable: &'tcx Path<'_>, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool { - if let Res::Local(id) = variable.res { - mutated_variables(expr, cx).map_or(true, |mutated| mutated.contains(&id)) - } else { - true - } +pub fn is_potentially_mutated<'tcx>(variable: HirId, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool { + mutated_variables(expr, cx).map_or(true, |mutated| mutated.contains(&variable)) } struct MutVarsDelegate { diff --git a/tests/ui/checked_unwrap/complex_conditionals.stderr b/tests/ui/checked_unwrap/complex_conditionals.stderr index 33bb5136ef8e7..46c6f69708eb7 100644 --- a/tests/ui/checked_unwrap/complex_conditionals.stderr +++ b/tests/ui/checked_unwrap/complex_conditionals.stderr @@ -1,4 +1,4 @@ -error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `unwrap` on `x` after checking its variant with `is_ok` --> $DIR/complex_conditionals.rs:8:9 | LL | if x.is_ok() && y.is_err() { @@ -11,6 +11,7 @@ note: the lint level is defined here | LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: try using `if let` or `match` error: this call to `unwrap_err()` will always panic --> $DIR/complex_conditionals.rs:9:9 @@ -36,7 +37,7 @@ LL | if x.is_ok() && y.is_err() { LL | y.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `unwrap_err` on `y` after checking its variant with `is_err` --> $DIR/complex_conditionals.rs:11:9 | LL | if x.is_ok() && y.is_err() { @@ -44,6 +45,8 @@ LL | if x.is_ok() && y.is_err() { ... LL | y.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ + | + = help: try using `if let` or `match` error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals.rs:25:9 @@ -54,7 +57,7 @@ LL | if x.is_ok() || y.is_ok() { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `unwrap_err` on `x` after checking its variant with `is_ok` --> $DIR/complex_conditionals.rs:26:9 | LL | if x.is_ok() || y.is_ok() { @@ -62,6 +65,8 @@ LL | if x.is_ok() || y.is_ok() { ... LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ + | + = help: try using `if let` or `match` error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals.rs:27:9 @@ -72,7 +77,7 @@ LL | if x.is_ok() || y.is_ok() { LL | y.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `unwrap_err` on `y` after checking its variant with `is_ok` --> $DIR/complex_conditionals.rs:28:9 | LL | if x.is_ok() || y.is_ok() { @@ -80,14 +85,18 @@ LL | if x.is_ok() || y.is_ok() { ... LL | y.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ + | + = help: try using `if let` or `match` -error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `unwrap` on `x` after checking its variant with `is_ok` --> $DIR/complex_conditionals.rs:32:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { | --------- the check is happening here LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ + | + = help: try using `if let` or `match` error: this call to `unwrap_err()` will always panic --> $DIR/complex_conditionals.rs:33:9 @@ -107,7 +116,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { LL | y.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `unwrap_err` on `y` after checking its variant with `is_ok` --> $DIR/complex_conditionals.rs:35:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -115,8 +124,10 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { ... LL | y.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ + | + = help: try using `if let` or `match` -error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `unwrap` on `z` after checking its variant with `is_err` --> $DIR/complex_conditionals.rs:36:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -124,6 +135,8 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { ... LL | z.unwrap(); // unnecessary | ^^^^^^^^^^ + | + = help: try using `if let` or `match` error: this call to `unwrap_err()` will always panic --> $DIR/complex_conditionals.rs:37:9 @@ -143,7 +156,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `unwrap_err` on `x` after checking its variant with `is_ok` --> $DIR/complex_conditionals.rs:46:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { @@ -151,8 +164,10 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { ... LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ + | + = help: try using `if let` or `match` -error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `unwrap` on `y` after checking its variant with `is_ok` --> $DIR/complex_conditionals.rs:47:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { @@ -160,6 +175,8 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { ... LL | y.unwrap(); // unnecessary | ^^^^^^^^^^ + | + = help: try using `if let` or `match` error: this call to `unwrap_err()` will always panic --> $DIR/complex_conditionals.rs:48:9 @@ -179,7 +196,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { LL | z.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `unwrap_err` on `z` after checking its variant with `is_err` --> $DIR/complex_conditionals.rs:50:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { @@ -187,6 +204,8 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { ... LL | z.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ + | + = help: try using `if let` or `match` error: aborting due to 20 previous errors diff --git a/tests/ui/checked_unwrap/complex_conditionals_nested.stderr b/tests/ui/checked_unwrap/complex_conditionals_nested.stderr index a01f7f956f629..542ab53300c02 100644 --- a/tests/ui/checked_unwrap/complex_conditionals_nested.stderr +++ b/tests/ui/checked_unwrap/complex_conditionals_nested.stderr @@ -1,8 +1,8 @@ -error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `unwrap` on `x` after checking its variant with `is_some` --> $DIR/complex_conditionals_nested.rs:8:13 | LL | if x.is_some() { - | ----------- the check is happening here + | -------------- help: try: `if let Some(..) = x` LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ | diff --git a/tests/ui/checked_unwrap/simple_conditionals.stderr b/tests/ui/checked_unwrap/simple_conditionals.stderr index 7127ff89b31db..82f269543800f 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.stderr +++ b/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -1,8 +1,8 @@ -error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `unwrap` on `x` after checking its variant with `is_some` --> $DIR/simple_conditionals.rs:39:9 | LL | if x.is_some() { - | ----------- the check is happening here + | -------------- help: try: `if let Some(..) = x` LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ | @@ -12,11 +12,11 @@ note: the lint level is defined here LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: you checked before that `expect()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `expect` on `x` after checking its variant with `is_some` --> $DIR/simple_conditionals.rs:40:9 | LL | if x.is_some() { - | ----------- the check is happening here + | -------------- help: try: `if let Some(..) = x` LL | x.unwrap(); // unnecessary LL | x.expect("an error message"); // unnecessary | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -53,20 +53,20 @@ LL | if x.is_none() { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `unwrap` on `x` after checking its variant with `is_none` --> $DIR/simple_conditionals.rs:48:9 | LL | if x.is_none() { - | ----------- the check is happening here + | -------------- help: try: `if let Some(..) = x` ... LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ -error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `unwrap` on `x` after checking its variant with `is_some` --> $DIR/simple_conditionals.rs:7:13 | LL | if $a.is_some() { - | ------------ the check is happening here + | --------------- help: try: `if let Some(..) = x` LL | $a.unwrap(); // unnecessary | ^^^^^^^^^^^ ... @@ -75,19 +75,19 @@ LL | m!(x); | = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) -error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `unwrap` on `x` after checking its variant with `is_ok` --> $DIR/simple_conditionals.rs:56:9 | LL | if x.is_ok() { - | --------- the check is happening here + | ------------ help: try: `if let Ok(..) = x` LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ -error: you checked before that `expect()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `expect` on `x` after checking its variant with `is_ok` --> $DIR/simple_conditionals.rs:57:9 | LL | if x.is_ok() { - | --------- the check is happening here + | ------------ help: try: `if let Ok(..) = x` LL | x.unwrap(); // unnecessary LL | x.expect("an error message"); // unnecessary | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -119,11 +119,11 @@ LL | if x.is_ok() { LL | x.expect("an error message"); // will panic | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `unwrap_err` on `x` after checking its variant with `is_ok` --> $DIR/simple_conditionals.rs:62:9 | LL | if x.is_ok() { - | --------- the check is happening here + | ------------ help: try: `if let Err(..) = x` ... LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ @@ -136,20 +136,20 @@ LL | if x.is_err() { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `unwrap_err` on `x` after checking its variant with `is_err` --> $DIR/simple_conditionals.rs:66:9 | LL | if x.is_err() { - | ---------- the check is happening here + | ------------- help: try: `if let Err(..) = x` LL | x.unwrap(); // will panic LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `unwrap` on `x` after checking its variant with `is_err` --> $DIR/simple_conditionals.rs:68:9 | LL | if x.is_err() { - | ---------- the check is happening here + | ------------- help: try: `if let Ok(..) = x` ... LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ From 265b8ec622e0ab9c65109ba6b98646ecf12868c7 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Fri, 3 Sep 2021 06:41:30 +0200 Subject: [PATCH 72/94] Reorder approx_consts constant to match rust docs --- clippy_lints/src/approx_const.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs index 6100f4e435a8a..b04c50e9d04d4 100644 --- a/clippy_lints/src/approx_const.rs +++ b/clippy_lints/src/approx_const.rs @@ -48,12 +48,12 @@ const KNOWN_CONSTS: [(f64, &str, usize); 18] = [ (f64::FRAC_PI_4, "FRAC_PI_4", 5), (f64::FRAC_PI_6, "FRAC_PI_6", 5), (f64::FRAC_PI_8, "FRAC_PI_8", 5), - (f64::LN_10, "LN_10", 5), (f64::LN_2, "LN_2", 5), - (f64::LOG10_E, "LOG10_E", 5), - (f64::LOG2_E, "LOG2_E", 5), + (f64::LN_10, "LN_10", 5), (f64::LOG2_10, "LOG2_10", 5), + (f64::LOG2_E, "LOG2_E", 5), (f64::LOG10_2, "LOG10_2", 5), + (f64::LOG10_E, "LOG10_E", 5), (f64::PI, "PI", 3), (f64::SQRT_2, "SQRT_2", 5), ]; From a23af89447d467c38ed2e5c9358cc81b47dc346d Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Fri, 3 Sep 2021 08:34:34 +0200 Subject: [PATCH 73/94] Make `approx_const` MSRV aware Fixes #7623. --- clippy_lints/src/approx_const.rs | 123 +++++++++++++++----------- clippy_lints/src/lib.rs | 2 +- clippy_utils/src/msrvs.rs | 1 + tests/ui/min_rust_version_attr.rs | 5 ++ tests/ui/min_rust_version_attr.stderr | 8 +- 5 files changed, 81 insertions(+), 58 deletions(-) diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs index b04c50e9d04d4..069a10e9b3aeb 100644 --- a/clippy_lints/src/approx_const.rs +++ b/clippy_lints/src/approx_const.rs @@ -1,8 +1,10 @@ use clippy_utils::diagnostics::span_lint; +use clippy_utils::{meets_msrv, msrvs}; use rustc_ast::ast::{FloatTy, LitFloatType, LitKind}; use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol; use std::f64::consts as f64; @@ -36,68 +38,83 @@ declare_clippy_lint! { "the approximate of a known float constant (in `std::fXX::consts`)" } -// Tuples are of the form (constant, name, min_digits) -const KNOWN_CONSTS: [(f64, &str, usize); 18] = [ - (f64::E, "E", 4), - (f64::FRAC_1_PI, "FRAC_1_PI", 4), - (f64::FRAC_1_SQRT_2, "FRAC_1_SQRT_2", 5), - (f64::FRAC_2_PI, "FRAC_2_PI", 5), - (f64::FRAC_2_SQRT_PI, "FRAC_2_SQRT_PI", 5), - (f64::FRAC_PI_2, "FRAC_PI_2", 5), - (f64::FRAC_PI_3, "FRAC_PI_3", 5), - (f64::FRAC_PI_4, "FRAC_PI_4", 5), - (f64::FRAC_PI_6, "FRAC_PI_6", 5), - (f64::FRAC_PI_8, "FRAC_PI_8", 5), - (f64::LN_2, "LN_2", 5), - (f64::LN_10, "LN_10", 5), - (f64::LOG2_10, "LOG2_10", 5), - (f64::LOG2_E, "LOG2_E", 5), - (f64::LOG10_2, "LOG10_2", 5), - (f64::LOG10_E, "LOG10_E", 5), - (f64::PI, "PI", 3), - (f64::SQRT_2, "SQRT_2", 5), +// Tuples are of the form (constant, name, min_digits, msrv) +const KNOWN_CONSTS: [(f64, &str, usize, Option); 18] = [ + (f64::E, "E", 4, None), + (f64::FRAC_1_PI, "FRAC_1_PI", 4, None), + (f64::FRAC_1_SQRT_2, "FRAC_1_SQRT_2", 5, None), + (f64::FRAC_2_PI, "FRAC_2_PI", 5, None), + (f64::FRAC_2_SQRT_PI, "FRAC_2_SQRT_PI", 5, None), + (f64::FRAC_PI_2, "FRAC_PI_2", 5, None), + (f64::FRAC_PI_3, "FRAC_PI_3", 5, None), + (f64::FRAC_PI_4, "FRAC_PI_4", 5, None), + (f64::FRAC_PI_6, "FRAC_PI_6", 5, None), + (f64::FRAC_PI_8, "FRAC_PI_8", 5, None), + (f64::LN_2, "LN_2", 5, None), + (f64::LN_10, "LN_10", 5, None), + (f64::LOG2_10, "LOG2_10", 5, Some(msrvs::LOG2_10)), + (f64::LOG2_E, "LOG2_E", 5, None), + (f64::LOG10_2, "LOG10_2", 5, Some(msrvs::LOG10_2)), + (f64::LOG10_E, "LOG10_E", 5, None), + (f64::PI, "PI", 3, None), + (f64::SQRT_2, "SQRT_2", 5, None), ]; -declare_lint_pass!(ApproxConstant => [APPROX_CONSTANT]); +pub struct ApproxConstant { + msrv: Option, +} -impl<'tcx> LateLintPass<'tcx> for ApproxConstant { - fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let ExprKind::Lit(lit) = &e.kind { - check_lit(cx, &lit.node, e); +impl ApproxConstant { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } + + fn check_lit(&self, cx: &LateContext<'_>, lit: &LitKind, e: &Expr<'_>) { + match *lit { + LitKind::Float(s, LitFloatType::Suffixed(fty)) => match fty { + FloatTy::F32 => self.check_known_consts(cx, e, s, "f32"), + FloatTy::F64 => self.check_known_consts(cx, e, s, "f64"), + }, + LitKind::Float(s, LitFloatType::Unsuffixed) => self.check_known_consts(cx, e, s, "f{32, 64}"), + _ => (), } } -} -fn check_lit(cx: &LateContext<'_>, lit: &LitKind, e: &Expr<'_>) { - match *lit { - LitKind::Float(s, LitFloatType::Suffixed(fty)) => match fty { - FloatTy::F32 => check_known_consts(cx, e, s, "f32"), - FloatTy::F64 => check_known_consts(cx, e, s, "f64"), - }, - LitKind::Float(s, LitFloatType::Unsuffixed) => check_known_consts(cx, e, s, "f{32, 64}"), - _ => (), + fn check_known_consts(&self, cx: &LateContext<'_>, e: &Expr<'_>, s: symbol::Symbol, module: &str) { + let s = s.as_str(); + if s.parse::().is_ok() { + for &(constant, name, min_digits, msrv) in &KNOWN_CONSTS { + if is_approx_const(constant, &s, min_digits) + && msrv.as_ref().map_or(true, |msrv| meets_msrv(self.msrv.as_ref(), msrv)) + { + span_lint( + cx, + APPROX_CONSTANT, + e.span, + &format!( + "approximate value of `{}::consts::{}` found. \ + Consider using it directly", + module, &name + ), + ); + return; + } + } + } } } -fn check_known_consts(cx: &LateContext<'_>, e: &Expr<'_>, s: symbol::Symbol, module: &str) { - let s = s.as_str(); - if s.parse::().is_ok() { - for &(constant, name, min_digits) in &KNOWN_CONSTS { - if is_approx_const(constant, &s, min_digits) { - span_lint( - cx, - APPROX_CONSTANT, - e.span, - &format!( - "approximate value of `{}::consts::{}` found. \ - Consider using it directly", - module, &name - ), - ); - return; - } +impl_lint_pass!(ApproxConstant => [APPROX_CONSTANT]); + +impl<'tcx> LateLintPass<'tcx> for ApproxConstant { + fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { + if let ExprKind::Lit(lit) = &e.kind { + self.check_lit(cx, &lit.node, e); } } + + extract_msrv_attr!(LateContext); } /// Returns `false` if the number of significant figures in `value` are diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2b76eacb7d61c..0d9b81e3d75ee 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1865,7 +1865,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(needless_bool::NeedlessBool)); store.register_late_pass(|| Box::new(needless_bool::BoolComparison)); store.register_late_pass(|| Box::new(needless_for_each::NeedlessForEach)); - store.register_late_pass(|| Box::new(approx_const::ApproxConstant)); store.register_late_pass(|| Box::new(misc::MiscLints)); store.register_late_pass(|| Box::new(eta_reduction::EtaReduction)); store.register_late_pass(|| Box::new(identity_op::IdentityOp)); @@ -1894,6 +1893,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: }); let avoid_breaking_exported_api = conf.avoid_breaking_exported_api; + store.register_late_pass(move || Box::new(approx_const::ApproxConstant::new(msrv))); store.register_late_pass(move || Box::new(methods::Methods::new(avoid_breaking_exported_api, msrv))); store.register_late_pass(move || Box::new(matches::Matches::new(msrv))); store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustive::new(msrv))); diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 14234d9c9cbf0..6fab17f07fe0b 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -17,6 +17,7 @@ msrv_aliases! { 1,50,0 { BOOL_THEN } 1,46,0 { CONST_IF_MATCH } 1,45,0 { STR_STRIP_PREFIX } + 1,43,0 { LOG2_10, LOG10_2 } 1,42,0 { MATCHES_MACRO } 1,41,0 { RE_REBALANCING_COHERENCE, RESULT_MAP_OR_ELSE } 1,40,0 { MEM_TAKE, NON_EXHAUSTIVE, OPTION_AS_DEREF } diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index 7f9f7ddc53575..8d9fc5a864d75 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -4,6 +4,11 @@ use std::ops::{Deref, RangeFrom}; +fn approx_const() { + let log2_10 = 3.321928094887362; + let log10_2 = 0.301029995663981; +} + fn cloned_instead_of_copied() { let _ = [1].iter().cloned(); } diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr index a2e4e86ed6b8c..360dcfb230c65 100644 --- a/tests/ui/min_rust_version_attr.stderr +++ b/tests/ui/min_rust_version_attr.stderr @@ -1,12 +1,12 @@ error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:160:24 + --> $DIR/min_rust_version_attr.rs:165:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::manual-strip` implied by `-D warnings` note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:159:9 + --> $DIR/min_rust_version_attr.rs:164:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,13 +17,13 @@ LL ~ assert_eq!(.to_uppercase(), "WORLD!"); | error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:172:24 + --> $DIR/min_rust_version_attr.rs:177:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:171:9 + --> $DIR/min_rust_version_attr.rs:176:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 9ebd012c7825b08c315a9f96c140ef9843bf3fbd Mon Sep 17 00:00:00 2001 From: xFrednet Date: Fri, 3 Sep 2021 12:12:16 +0200 Subject: [PATCH 74/94] Updating issue templates again for rustbot --- .github/ISSUE_TEMPLATE/blank_issue.md | 5 +++-- .github/ISSUE_TEMPLATE/bug_report.md | 5 +++-- .github/ISSUE_TEMPLATE/false_positive.md | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/blank_issue.md b/.github/ISSUE_TEMPLATE/blank_issue.md index 5d7dfa36f37a2..2891d5e5da1e1 100644 --- a/.github/ISSUE_TEMPLATE/blank_issue.md +++ b/.github/ISSUE_TEMPLATE/blank_issue.md @@ -5,9 +5,10 @@ about: Create a blank issue. $DIR/match_same_arms.rs:33:14 - | -LL | 3 => 2, //~ ERROR 3rd matched arms have same body - | ^ - | -note: same as this - --> $DIR/match_same_arms.rs:32:14 - | -LL | 2 => 2, //~ ERROR 2nd matched arms have same body - | ^ -help: consider refactoring into `2 | 3` - --> $DIR/match_same_arms.rs:32:9 - | -LL | 2 => 2, //~ ERROR 2nd matched arms have same body - | ^ - = help: ...or consider changing the match arm bodies - error: this `match` has identical arm bodies --> $DIR/match_same_arms.rs:50:55 | @@ -142,5 +124,5 @@ LL | CommandInfo::BuiltIn { name, .. } => name.to_string(), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: ...or consider changing the match arm bodies -error: aborting due to 8 previous errors +error: aborting due to 7 previous errors diff --git a/tests/ui/modulo_one.stderr b/tests/ui/modulo_one.stderr index 2b2c699733852..34f762bbb9236 100644 --- a/tests/ui/modulo_one.stderr +++ b/tests/ui/modulo_one.stderr @@ -46,12 +46,6 @@ LL | const ONE: u32 = 1 * 1; | = note: `-D clippy::identity-op` implied by `-D warnings` -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/modulo_one.rs:13:22 - | -LL | const ONE: u32 = 1 * 1; - | ^^^^^ - error: any number modulo 1 will be 0 --> $DIR/modulo_one.rs:17:5 | @@ -70,5 +64,5 @@ error: any number modulo -1 will panic/overflow or result in 0 LL | INT_MIN % NEG_ONE; // also caught by rustc | ^^^^^^^^^^^^^^^^^ -error: aborting due to 11 previous errors +error: aborting due to 10 previous errors diff --git a/tests/ui/suspicious_operation_groupings.stderr b/tests/ui/suspicious_operation_groupings.stderr index 96065699d321a..dd6f2f6641d67 100644 --- a/tests/ui/suspicious_operation_groupings.stderr +++ b/tests/ui/suspicious_operation_groupings.stderr @@ -6,12 +6,6 @@ LL | self.x == other.y && self.y == other.y && self.z == other.z | = note: `-D clippy::suspicious-operation-groupings` implied by `-D warnings` -error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:14:9 - | -LL | self.x == other.y && self.y == other.y && self.z == other.z - | ^^^^^^^^^^^^^^^^^ help: did you mean: `self.x == other.x` - error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:27:20 | @@ -162,5 +156,5 @@ error: this sequence of operators looks suspiciously like a bug LL | -(if -s1.a < -s2.a && -s1.a < -s2.b { s1.c } else { s2.a }) | ^^^^^^^^^^^^^ help: did you mean: `-s1.b < -s2.b` -error: aborting due to 27 previous errors +error: aborting due to 26 previous errors From b2f01ef6f600b249416cf55426716091582a3a76 Mon Sep 17 00:00:00 2001 From: chansuke Date: Sat, 4 Sep 2021 21:07:25 +0900 Subject: [PATCH 77/94] Fix documentation of eval_order_dependence --- clippy_lints/src/eval_order_dependence.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/eval_order_dependence.rs b/clippy_lints/src/eval_order_dependence.rs index f72a1e446d55c..8714ce90164c8 100644 --- a/clippy_lints/src/eval_order_dependence.rs +++ b/clippy_lints/src/eval_order_dependence.rs @@ -15,8 +15,8 @@ declare_clippy_lint! { /// order of sub-expressions. /// /// ### Why is this bad? - /// It is often confusing to read. In addition, the - /// sub-expression evaluation order for Rust is not well documented. + /// It is often confusing to read. As described [here](https://doc.rust-lang.org/reference/expressions.html?highlight=subexpression#evaluation-order-of-operands), + /// the operands of these expressions are evaluated before applying the effects of the expression. /// /// ### Known problems /// Code which intentionally depends on the evaluation From 4184cc369a8e08bc74560baf0f47bab14423e26e Mon Sep 17 00:00:00 2001 From: lengyijun Date: Tue, 24 Aug 2021 22:06:46 +0800 Subject: [PATCH 78/94] needless_option_as_deref --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 ++ clippy_lints/src/loops/never_loop.rs | 4 +- clippy_lints/src/needless_option_as_deref.rs | 69 ++++++++++++++++++++ tests/ui/needless_option_as_deref.fixed | 13 ++++ tests/ui/needless_option_as_deref.rs | 13 ++++ tests/ui/needless_option_as_deref.stderr | 16 +++++ 7 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 clippy_lints/src/needless_option_as_deref.rs create mode 100644 tests/ui/needless_option_as_deref.fixed create mode 100644 tests/ui/needless_option_as_deref.rs create mode 100644 tests/ui/needless_option_as_deref.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index d86414a87f4d1..0e131ed02af04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2822,6 +2822,7 @@ Released 2018-09-13 [`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main [`needless_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each [`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes +[`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref [`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value [`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark [`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2b76eacb7d61c..64b7ff199be6c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -289,6 +289,7 @@ mod needless_borrow; mod needless_borrowed_ref; mod needless_continue; mod needless_for_each; +mod needless_option_as_deref; mod needless_pass_by_value; mod needless_question_mark; mod needless_update; @@ -847,6 +848,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, needless_continue::NEEDLESS_CONTINUE, needless_for_each::NEEDLESS_FOR_EACH, + needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF, needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, needless_question_mark::NEEDLESS_QUESTION_MARK, needless_update::NEEDLESS_UPDATE, @@ -1374,6 +1376,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(needless_bool::NEEDLESS_BOOL), LintId::of(needless_borrow::NEEDLESS_BORROW), LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), + LintId::of(needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF), LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK), LintId::of(needless_update::NEEDLESS_UPDATE), LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), @@ -1636,6 +1639,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(needless_bool::BOOL_COMPARISON), LintId::of(needless_bool::NEEDLESS_BOOL), LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), + LintId::of(needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF), LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK), LintId::of(needless_update::NEEDLESS_UPDATE), LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), @@ -1863,6 +1867,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(ptr::Ptr)); store.register_late_pass(|| Box::new(ptr_eq::PtrEq)); store.register_late_pass(|| Box::new(needless_bool::NeedlessBool)); + store.register_late_pass(|| Box::new(needless_option_as_deref::OptionNeedlessDeref)); store.register_late_pass(|| Box::new(needless_bool::BoolComparison)); store.register_late_pass(|| Box::new(needless_for_each::NeedlessForEach)); store.register_late_pass(|| Box::new(approx_const::ApproxConstant)); diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 2c46971d5f741..41956650c9f4e 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -87,7 +87,7 @@ fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult fn never_loop_block(block: &Block<'_>, main_loop_id: HirId) -> NeverLoopResult { let stmts = block.stmts.iter().map(stmt_to_expr); - let expr = once(block.expr.as_deref()); + let expr = once(block.expr); let mut iter = stmts.chain(expr).flatten(); never_loop_expr_seq(&mut iter, main_loop_id) } @@ -100,7 +100,7 @@ fn never_loop_expr_seq<'a, T: Iterator>>(es: &mut T, main_lo fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<&'tcx Expr<'tcx>> { match stmt.kind { StmtKind::Semi(e, ..) | StmtKind::Expr(e, ..) => Some(e), - StmtKind::Local(local) => local.init.as_deref(), + StmtKind::Local(local) => local.init, StmtKind::Item(..) => None, } } diff --git a/clippy_lints/src/needless_option_as_deref.rs b/clippy_lints/src/needless_option_as_deref.rs new file mode 100644 index 0000000000000..67c697da04f40 --- /dev/null +++ b/clippy_lints/src/needless_option_as_deref.rs @@ -0,0 +1,69 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::in_macro; +use clippy_utils::source::snippet_opt; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyS; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::sym; + +declare_clippy_lint! { + /// ### What it does + /// Checks for no-op uses of Option::{as_deref,as_deref_mut}, + /// for example, `Option<&T>::as_deref()` returns the same type. + /// + /// ### Why is this bad? + /// Redundant code and improving readability. + /// + /// ### Example + /// ```rust + /// let a = Some(&1); + /// let b = a.as_deref(); // goes from Option<&i32> to Option<&i32> + /// ``` + /// Could be written as: + /// ```rust + /// let a = Some(&1); + /// let b = a; + /// ``` + pub NEEDLESS_OPTION_AS_DEREF, + complexity, + "no-op use of `deref` or `deref_mut` method to `Option`." +} + +declare_lint_pass!(OptionNeedlessDeref=> [ + NEEDLESS_OPTION_AS_DEREF, +]); + +impl<'tcx> LateLintPass<'tcx> for OptionNeedlessDeref { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if expr.span.from_expansion() { + return; + } + if in_macro(expr.span) { + return; + } + let typeck = cx.typeck_results(); + let outer_ty = typeck.expr_ty(expr); + + if_chain! { + if is_type_diagnostic_item(cx,outer_ty,sym::option_type); + if let ExprKind::MethodCall(path, _, [sub_expr], _) = expr.kind; + let symbol = path.ident.as_str(); + if symbol=="as_deref" || symbol=="as_deref_mut"; + if TyS::same_type( outer_ty, typeck.expr_ty(sub_expr) ); + then{ + span_lint_and_sugg( + cx, + NEEDLESS_OPTION_AS_DEREF, + expr.span, + "derefed type is same as origin", + "try this", + snippet_opt(cx,sub_expr.span).unwrap(), + Applicability::MachineApplicable + ); + } + } + } +} diff --git a/tests/ui/needless_option_as_deref.fixed b/tests/ui/needless_option_as_deref.fixed new file mode 100644 index 0000000000000..d721452ae8880 --- /dev/null +++ b/tests/ui/needless_option_as_deref.fixed @@ -0,0 +1,13 @@ +// run-rustfix + +#[warn(clippy::needless_option_as_deref)] + +fn main() { + // should lint + let _: Option<&usize> = Some(&1); + let _: Option<&mut usize> = Some(&mut 1); + + // should not lint + let _ = Some(Box::new(1)).as_deref(); + let _ = Some(Box::new(1)).as_deref_mut(); +} diff --git a/tests/ui/needless_option_as_deref.rs b/tests/ui/needless_option_as_deref.rs new file mode 100644 index 0000000000000..bb15512adf6aa --- /dev/null +++ b/tests/ui/needless_option_as_deref.rs @@ -0,0 +1,13 @@ +// run-rustfix + +#[warn(clippy::needless_option_as_deref)] + +fn main() { + // should lint + let _: Option<&usize> = Some(&1).as_deref(); + let _: Option<&mut usize> = Some(&mut 1).as_deref_mut(); + + // should not lint + let _ = Some(Box::new(1)).as_deref(); + let _ = Some(Box::new(1)).as_deref_mut(); +} diff --git a/tests/ui/needless_option_as_deref.stderr b/tests/ui/needless_option_as_deref.stderr new file mode 100644 index 0000000000000..5dd507b4a71e8 --- /dev/null +++ b/tests/ui/needless_option_as_deref.stderr @@ -0,0 +1,16 @@ +error: derefed type is same as origin + --> $DIR/needless_option_as_deref.rs:7:29 + | +LL | let _: Option<&usize> = Some(&1).as_deref(); + | ^^^^^^^^^^^^^^^^^^^ help: try this: `Some(&1)` + | + = note: `-D clippy::needless-option-as-deref` implied by `-D warnings` + +error: derefed type is same as origin + --> $DIR/needless_option_as_deref.rs:8:33 + | +LL | let _: Option<&mut usize> = Some(&mut 1).as_deref_mut(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Some(&mut 1)` + +error: aborting due to 2 previous errors + From 8221f9e7950120c940511ca914c9724cc89b56d5 Mon Sep 17 00:00:00 2001 From: hamidreza kalbasi Date: Mon, 16 Aug 2021 19:34:41 +0430 Subject: [PATCH 79/94] add derivable impls lint --- CHANGELOG.md | 1 + clippy_lints/src/derivable_impls.rs | 108 ++++++++++++++++++ clippy_lints/src/lib.rs | 5 + clippy_lints/src/mem_replace.rs | 85 +++++--------- clippy_utils/src/lib.rs | 61 +++++++++- clippy_utils/src/ty.rs | 7 ++ tests/ui/derivable_impls.rs | 170 ++++++++++++++++++++++++++++ tests/ui/derivable_impls.stderr | 77 +++++++++++++ tests/ui/mem_replace.fixed | 20 ++++ tests/ui/mem_replace.rs | 20 ++++ tests/ui/mem_replace.stderr | 20 +++- 11 files changed, 516 insertions(+), 58 deletions(-) create mode 100644 clippy_lints/src/derivable_impls.rs create mode 100644 tests/ui/derivable_impls.rs create mode 100644 tests/ui/derivable_impls.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index d86414a87f4d1..57df49a2723b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2613,6 +2613,7 @@ Released 2018-09-13 [`deprecated_cfg_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr [`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver [`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof +[`derivable_impls`]: https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls [`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq [`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord [`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs new file mode 100644 index 0000000000000..b4c4ca016aace --- /dev/null +++ b/clippy_lints/src/derivable_impls.rs @@ -0,0 +1,108 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::{in_macro, is_automatically_derived, is_default_equivalent, remove_blocks}; +use rustc_hir::{ + def::{DefKind, Res}, + Body, Expr, ExprKind, Impl, ImplItemKind, Item, ItemKind, Node, QPath, +}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TypeFoldable; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Detects manual `std::default::Default` implementations that are identical to a derived implementation. + /// + /// ### Why is this bad? + /// It is less concise. + /// + /// ### Example + /// ```rust + /// struct Foo { + /// bar: bool + /// } + /// + /// impl std::default::Default for Foo { + /// fn default() -> Self { + /// Self { + /// bar: false + /// } + /// } + /// } + /// ``` + /// + /// Could be written as: + /// + /// ```rust + /// #[derive(Default)] + /// struct Foo { + /// bar: bool + /// } + /// ``` + /// + /// ### Known problems + /// Derive macros [sometimes use incorrect bounds](https://github.com/rust-lang/rust/issues/26925) + /// in generic types and the user defined `impl` maybe is more generalized or + /// specialized than what derive will produce. This lint can't detect the manual `impl` + /// has exactly equal bounds, and therefore this lint is disabled for types with + /// generic parameters. + /// + pub DERIVABLE_IMPLS, + complexity, + "manual implementation of the `Default` trait which is equal to a derive" +} + +declare_lint_pass!(DerivableImpls => [DERIVABLE_IMPLS]); + +fn is_path_self(e: &Expr<'_>) -> bool { + if let ExprKind::Path(QPath::Resolved(_, p)) = e.kind { + matches!(p.res, Res::SelfCtor(..) | Res::Def(DefKind::Ctor(..), _)) + } else { + false + } +} + +impl<'tcx> LateLintPass<'tcx> for DerivableImpls { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if_chain! { + if let ItemKind::Impl(Impl { + of_trait: Some(ref trait_ref), + items: [child], + .. + }) = item.kind; + if let attrs = cx.tcx.hir().attrs(item.hir_id()); + if !is_automatically_derived(attrs); + if !in_macro(item.span); + if let Some(def_id) = trait_ref.trait_def_id(); + if cx.tcx.is_diagnostic_item(sym::Default, def_id); + if let impl_item_hir = child.id.hir_id(); + if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir); + if let ImplItemKind::Fn(_, b) = &impl_item.kind; + if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b); + if let Some(adt_def) = cx.tcx.type_of(item.def_id).ty_adt_def(); + then { + if cx.tcx.type_of(item.def_id).definitely_has_param_types_or_consts(cx.tcx) { + return; + } + let should_emit = match remove_blocks(func_expr).kind { + ExprKind::Tup(fields) => fields.iter().all(|e| is_default_equivalent(cx, e)), + ExprKind::Call(callee, args) + if is_path_self(callee) => args.iter().all(|e| is_default_equivalent(cx, e)), + ExprKind::Struct(_, fields, _) => fields.iter().all(|ef| is_default_equivalent(cx, ef.expr)), + _ => false, + }; + if should_emit { + let path_string = cx.tcx.def_path_str(adt_def.did); + span_lint_and_help( + cx, + DERIVABLE_IMPLS, + item.span, + "this `impl` can be derived", + None, + &format!("try annotating `{}` with `#[derive(Default)]`", path_string), + ); + } + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2b76eacb7d61c..88c1ee1ba0e36 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -187,6 +187,7 @@ mod dbg_macro; mod default; mod default_numeric_fallback; mod dereference; +mod derivable_impls; mod derive; mod disallowed_method; mod disallowed_script_idents; @@ -586,6 +587,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: default::FIELD_REASSIGN_WITH_DEFAULT, default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK, dereference::EXPLICIT_DEREF_METHODS, + derivable_impls::DERIVABLE_IMPLS, derive::DERIVE_HASH_XOR_EQ, derive::DERIVE_ORD_XOR_PARTIAL_ORD, derive::EXPL_IMPL_CLONE_ON_COPY, @@ -1204,6 +1206,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(copies::IFS_SAME_COND), LintId::of(copies::IF_SAME_THEN_ELSE), LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), + LintId::of(derivable_impls::DERIVABLE_IMPLS), LintId::of(derive::DERIVE_HASH_XOR_EQ), LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD), LintId::of(doc::MISSING_SAFETY_DOC), @@ -1589,6 +1592,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(booleans::NONMINIMAL_BOOL), LintId::of(casts::CHAR_LIT_AS_U8), LintId::of(casts::UNNECESSARY_CAST), + LintId::of(derivable_impls::DERIVABLE_IMPLS), LintId::of(double_comparison::DOUBLE_COMPARISONS), LintId::of(double_parens::DOUBLE_PARENS), LintId::of(duration_subsec::DURATION_SUBSEC), @@ -1937,6 +1941,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(panic_unimplemented::PanicUnimplemented)); store.register_late_pass(|| Box::new(strings::StringLitAsBytes)); store.register_late_pass(|| Box::new(derive::Derive)); + store.register_late_pass(|| Box::new(derivable_impls::DerivableImpls)); store.register_late_pass(|| Box::new(get_last_with_len::GetLastWithLen)); store.register_late_pass(|| Box::new(drop_forget_ref::DropForgetRef)); store.register_late_pass(|| Box::new(empty_enum::EmptyEnum)); diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index 3d071c9081be1..1e6057a8fe969 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_applicability}; -use clippy_utils::{in_macro, is_diag_trait_item, is_lang_ctor, match_def_path, meets_msrv, msrvs, paths}; +use clippy_utils::ty::is_non_aggregate_primitive_type; +use clippy_utils::{in_macro, is_default_equivalent, is_lang_ctor, match_def_path, meets_msrv, msrvs, paths}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::def_id::DefId; use rustc_hir::LangItem::OptionNone; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -194,64 +194,37 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<' } } -/// Returns true if the `def_id` associated with the `path` is recognized as a "default-equivalent" -/// constructor from the std library -fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool { - let std_types_symbols = &[ - sym::string_type, - sym::vec_type, - sym::vecdeque_type, - sym::LinkedList, - sym::hashmap_type, - sym::BTreeMap, - sym::hashset_type, - sym::BTreeSet, - sym::BinaryHeap, - ]; - - if let QPath::TypeRelative(_, method) = path { - if method.ident.name == sym::new { - if let Some(impl_did) = cx.tcx.impl_of_method(def_id) { - if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() { - return std_types_symbols - .iter() - .any(|&symbol| cx.tcx.is_diagnostic_item(symbol, adt.did)); - } - } +fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) { + // disable lint for primitives + let expr_type = cx.typeck_results().expr_ty_adjusted(src); + if is_non_aggregate_primitive_type(expr_type) { + return; + } + // disable lint for Option since it is covered in another lint + if let ExprKind::Path(q) = &src.kind { + if is_lang_ctor(cx, q, OptionNone) { + return; } } - false -} + if is_default_equivalent(cx, src) && !in_external_macro(cx.tcx.sess, expr_span) { + span_lint_and_then( + cx, + MEM_REPLACE_WITH_DEFAULT, + expr_span, + "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`", + |diag| { + if !in_macro(expr_span) { + let suggestion = format!("std::mem::take({})", snippet(cx, dest.span, "")); -fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) { - if_chain! { - if let ExprKind::Call(repl_func, _) = src.kind; - if !in_external_macro(cx.tcx.sess, expr_span); - if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind; - if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id(); - if is_diag_trait_item(cx, repl_def_id, sym::Default) - || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath); - - then { - span_lint_and_then( - cx, - MEM_REPLACE_WITH_DEFAULT, - expr_span, - "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`", - |diag| { - if !in_macro(expr_span) { - let suggestion = format!("std::mem::take({})", snippet(cx, dest.span, "")); - - diag.span_suggestion( - expr_span, - "consider using", - suggestion, - Applicability::MachineApplicable - ); - } + diag.span_suggestion( + expr_span, + "consider using", + suggestion, + Applicability::MachineApplicable, + ); } - ); - } + }, + ); } } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 40d7963a5e545..757485d19d25a 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -70,7 +70,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{self, walk_expr, ErasedMap, FnKind, NestedVisitorMap, Visitor}; -use rustc_hir::LangItem::{ResultErr, ResultOk}; +use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk}; use rustc_hir::{ def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Mutability, Node, Param, Pat, @@ -644,6 +644,65 @@ pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) - false } +/// Returns true if the `def_id` associated with the `path` is recognized as a "default-equivalent" +/// constructor from the std library +fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool { + let std_types_symbols = &[ + sym::string_type, + sym::vec_type, + sym::vecdeque_type, + sym::LinkedList, + sym::hashmap_type, + sym::BTreeMap, + sym::hashset_type, + sym::BTreeSet, + sym::BinaryHeap, + ]; + + if let QPath::TypeRelative(_, method) = path { + if method.ident.name == sym::new { + if let Some(impl_did) = cx.tcx.impl_of_method(def_id) { + if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() { + return std_types_symbols + .iter() + .any(|&symbol| cx.tcx.is_diagnostic_item(symbol, adt.did)); + } + } + } + } + false +} + +/// Returns true if the expr is equal to `Default::default()` of it's type when evaluated. +/// It doesn't cover all cases, for example indirect function calls (some of std +/// functions are supported) but it is the best we have. +pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { + match &e.kind { + ExprKind::Lit(lit) => match lit.node { + LitKind::Bool(false) | LitKind::Int(0, _) => true, + LitKind::Str(s, _) => s.is_empty(), + _ => false, + }, + ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)), + ExprKind::Repeat(x, _) => is_default_equivalent(cx, x), + ExprKind::Call(repl_func, _) => if_chain! { + if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind; + if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id(); + if is_diag_trait_item(cx, repl_def_id, sym::Default) + || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath); + then { + true + } + else { + false + } + }, + ExprKind::Path(qpath) => is_lang_ctor(cx, qpath, OptionNone), + ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])), + _ => false, + } +} + /// Checks if the top level expression can be moved into a closure as is. /// Currently checks for: /// * Break/Continue outside the given loop HIR ids. diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 7f7fbdc9f2142..d6f9ebe89bc7f 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -224,6 +224,13 @@ fn is_normalizable_helper<'tcx>( result } +/// Returns true iff the given type is a non aggregate primitive (a bool or char, any integer or +/// floating-point number type). For checking aggregation of primitive types (e.g. tuples and slices +/// of primitive type) see `is_recursively_primitive_type` +pub fn is_non_aggregate_primitive_type(ty: Ty<'_>) -> bool { + matches!(ty.kind(), ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_)) +} + /// Returns true iff the given type is a primitive (a bool or char, any integer or floating-point /// number type, a str, or an array, slice, or tuple of those types). pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool { diff --git a/tests/ui/derivable_impls.rs b/tests/ui/derivable_impls.rs new file mode 100644 index 0000000000000..336a743de726b --- /dev/null +++ b/tests/ui/derivable_impls.rs @@ -0,0 +1,170 @@ +use std::collections::HashMap; + +struct FooDefault<'a> { + a: bool, + b: i32, + c: u64, + d: Vec, + e: FooND1, + f: FooND2, + g: HashMap, + h: (i32, Vec), + i: [Vec; 3], + j: [i32; 5], + k: Option, + l: &'a [i32], +} + +impl std::default::Default for FooDefault<'_> { + fn default() -> Self { + Self { + a: false, + b: 0, + c: 0u64, + d: vec![], + e: Default::default(), + f: FooND2::default(), + g: HashMap::new(), + h: (0, vec![]), + i: [vec![], vec![], vec![]], + j: [0; 5], + k: None, + l: &[], + } + } +} + +struct TupleDefault(bool, i32, u64); + +impl std::default::Default for TupleDefault { + fn default() -> Self { + Self(false, 0, 0u64) + } +} + +struct FooND1 { + a: bool, +} + +impl std::default::Default for FooND1 { + fn default() -> Self { + Self { a: true } + } +} + +struct FooND2 { + a: i32, +} + +impl std::default::Default for FooND2 { + fn default() -> Self { + Self { a: 5 } + } +} + +struct FooNDNew { + a: bool, +} + +impl FooNDNew { + fn new() -> Self { + Self { a: true } + } +} + +impl Default for FooNDNew { + fn default() -> Self { + Self::new() + } +} + +struct FooNDVec(Vec); + +impl Default for FooNDVec { + fn default() -> Self { + Self(vec![5, 12]) + } +} + +struct StrDefault<'a>(&'a str); + +impl Default for StrDefault<'_> { + fn default() -> Self { + Self("") + } +} + +#[derive(Default)] +struct AlreadyDerived(i32, bool); + +macro_rules! mac { + () => { + 0 + }; + ($e:expr) => { + struct X(u32); + impl Default for X { + fn default() -> Self { + Self($e) + } + } + }; +} + +mac!(0); + +struct Y(u32); +impl Default for Y { + fn default() -> Self { + Self(mac!()) + } +} + +struct RustIssue26925 { + a: Option, +} + +// We should watch out for cases where a manual impl is needed because a +// derive adds different type bounds (https://github.com/rust-lang/rust/issues/26925). +// For example, a struct with Option does not require T: Default, but a derive adds +// that type bound anyways. So until #26925 get fixed we should disable lint +// for the following case +impl Default for RustIssue26925 { + fn default() -> Self { + Self { a: None } + } +} + +struct SpecializedImpl { + a: A, + b: B, +} + +impl Default for SpecializedImpl { + fn default() -> Self { + Self { + a: T::default(), + b: T::default(), + } + } +} + +struct WithoutSelfCurly { + a: bool, +} + +impl Default for WithoutSelfCurly { + fn default() -> Self { + WithoutSelfCurly { a: false } + } +} + +struct WithoutSelfParan(bool); + +impl Default for WithoutSelfParan { + fn default() -> Self { + WithoutSelfParan(false) + } +} + +fn main() {} diff --git a/tests/ui/derivable_impls.stderr b/tests/ui/derivable_impls.stderr new file mode 100644 index 0000000000000..4ed64fade026d --- /dev/null +++ b/tests/ui/derivable_impls.stderr @@ -0,0 +1,77 @@ +error: this `impl` can be derived + --> $DIR/derivable_impls.rs:18:1 + | +LL | / impl std::default::Default for FooDefault<'_> { +LL | | fn default() -> Self { +LL | | Self { +LL | | a: false, +... | +LL | | } +LL | | } + | |_^ + | + = note: `-D clippy::derivable-impls` implied by `-D warnings` + = help: try annotating `FooDefault` with `#[derive(Default)]` + +error: this `impl` can be derived + --> $DIR/derivable_impls.rs:39:1 + | +LL | / impl std::default::Default for TupleDefault { +LL | | fn default() -> Self { +LL | | Self(false, 0, 0u64) +LL | | } +LL | | } + | |_^ + | + = help: try annotating `TupleDefault` with `#[derive(Default)]` + +error: this `impl` can be derived + --> $DIR/derivable_impls.rs:91:1 + | +LL | / impl Default for StrDefault<'_> { +LL | | fn default() -> Self { +LL | | Self("") +LL | | } +LL | | } + | |_^ + | + = help: try annotating `StrDefault` with `#[derive(Default)]` + +error: this `impl` can be derived + --> $DIR/derivable_impls.rs:117:1 + | +LL | / impl Default for Y { +LL | | fn default() -> Self { +LL | | Self(mac!()) +LL | | } +LL | | } + | |_^ + | + = help: try annotating `Y` with `#[derive(Default)]` + +error: this `impl` can be derived + --> $DIR/derivable_impls.rs:156:1 + | +LL | / impl Default for WithoutSelfCurly { +LL | | fn default() -> Self { +LL | | WithoutSelfCurly { a: false } +LL | | } +LL | | } + | |_^ + | + = help: try annotating `WithoutSelfCurly` with `#[derive(Default)]` + +error: this `impl` can be derived + --> $DIR/derivable_impls.rs:164:1 + | +LL | / impl Default for WithoutSelfParan { +LL | | fn default() -> Self { +LL | | WithoutSelfParan(false) +LL | | } +LL | | } + | |_^ + | + = help: try annotating `WithoutSelfParan` with `#[derive(Default)]` + +error: aborting due to 6 previous errors + diff --git a/tests/ui/mem_replace.fixed b/tests/ui/mem_replace.fixed index 3b6224254a0a7..b609ba659467f 100644 --- a/tests/ui/mem_replace.fixed +++ b/tests/ui/mem_replace.fixed @@ -51,9 +51,29 @@ fn replace_with_default() { let mut binary_heap: BinaryHeap = BinaryHeap::new(); let _ = std::mem::take(&mut binary_heap); + + let mut tuple = (vec![1, 2], BinaryHeap::::new()); + let _ = std::mem::take(&mut tuple); + + let mut refstr = "hello"; + let _ = std::mem::take(&mut refstr); + + let mut slice: &[i32] = &[1, 2, 3]; + let _ = std::mem::take(&mut slice); +} + +// lint is disabled for primitives because in this case `take` +// has no clear benefit over `replace` and sometimes is harder to read +fn dont_lint_primitive() { + let mut pbool = true; + let _ = std::mem::replace(&mut pbool, false); + + let mut pint = 5; + let _ = std::mem::replace(&mut pint, 0); } fn main() { replace_option_with_none(); replace_with_default(); + dont_lint_primitive(); } diff --git a/tests/ui/mem_replace.rs b/tests/ui/mem_replace.rs index 0a36db9e92159..93f6dcdec83b9 100644 --- a/tests/ui/mem_replace.rs +++ b/tests/ui/mem_replace.rs @@ -51,9 +51,29 @@ fn replace_with_default() { let mut binary_heap: BinaryHeap = BinaryHeap::new(); let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new()); + + let mut tuple = (vec![1, 2], BinaryHeap::::new()); + let _ = std::mem::replace(&mut tuple, (vec![], BinaryHeap::new())); + + let mut refstr = "hello"; + let _ = std::mem::replace(&mut refstr, ""); + + let mut slice: &[i32] = &[1, 2, 3]; + let _ = std::mem::replace(&mut slice, &[]); +} + +// lint is disabled for primitives because in this case `take` +// has no clear benefit over `replace` and sometimes is harder to read +fn dont_lint_primitive() { + let mut pbool = true; + let _ = std::mem::replace(&mut pbool, false); + + let mut pint = 5; + let _ = std::mem::replace(&mut pint, 0); } fn main() { replace_option_with_none(); replace_with_default(); + dont_lint_primitive(); } diff --git a/tests/ui/mem_replace.stderr b/tests/ui/mem_replace.stderr index f8aa1538bffa8..90dc6c95f8582 100644 --- a/tests/ui/mem_replace.stderr +++ b/tests/ui/mem_replace.stderr @@ -98,5 +98,23 @@ error: replacing a value of type `T` with `T::default()` is better expressed usi LL | let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut binary_heap)` -error: aborting due to 16 previous errors +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:56:13 + | +LL | let _ = std::mem::replace(&mut tuple, (vec![], BinaryHeap::new())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut tuple)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:59:13 + | +LL | let _ = std::mem::replace(&mut refstr, ""); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut refstr)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:62:13 + | +LL | let _ = std::mem::replace(&mut slice, &[]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut slice)` + +error: aborting due to 19 previous errors From 37af742bbbd20b5fe5a753c0c23e5bab477298f1 Mon Sep 17 00:00:00 2001 From: lyj Date: Sun, 5 Sep 2021 08:33:04 +0800 Subject: [PATCH 80/94] Update clippy_lints/src/needless_option_as_deref.rs Co-authored-by: llogiq --- clippy_lints/src/needless_option_as_deref.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/clippy_lints/src/needless_option_as_deref.rs b/clippy_lints/src/needless_option_as_deref.rs index 67c697da04f40..5024a881d2aa8 100644 --- a/clippy_lints/src/needless_option_as_deref.rs +++ b/clippy_lints/src/needless_option_as_deref.rs @@ -38,10 +38,7 @@ declare_lint_pass!(OptionNeedlessDeref=> [ impl<'tcx> LateLintPass<'tcx> for OptionNeedlessDeref { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if expr.span.from_expansion() { - return; - } - if in_macro(expr.span) { + if expr.span.from_expansion() || in_macro(expr.span) { return; } let typeck = cx.typeck_results(); From 4ee9ec960ec434e27cf0764a539a3f42a43a896d Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Sun, 5 Sep 2021 08:19:30 +0200 Subject: [PATCH 81/94] Add MSRV to `approx_constant` documentation --- clippy_lints/src/utils/conf.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 881dc3219308b..8651fa6fcf9ea 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -132,7 +132,7 @@ define_Conf! { /// /// Suppress lints whenever the suggested change would cause breakage for other crates. (avoid_breaking_exported_api: bool = true), - /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT. /// /// The minimum rust version that the project supports (msrv: Option = None), From 19d8a3e53c77a51f691f61e1d4eb0ae2d0c94267 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Sun, 5 Sep 2021 08:42:00 +0200 Subject: [PATCH 82/94] Improve `approx_constant` output --- clippy_lints/src/approx_const.rs | 12 ++--- tests/ui/approx_const.stderr | 83 ++++++++++++++++++++++++-------- tests/ui/proc_macro.stderr | 3 +- 3 files changed, 69 insertions(+), 29 deletions(-) diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs index 069a10e9b3aeb..a57d8b67ed37d 100644 --- a/clippy_lints/src/approx_const.rs +++ b/clippy_lints/src/approx_const.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::{meets_msrv, msrvs}; use rustc_ast::ast::{FloatTy, LitFloatType, LitKind}; use rustc_hir::{Expr, ExprKind}; @@ -88,15 +88,13 @@ impl ApproxConstant { if is_approx_const(constant, &s, min_digits) && msrv.as_ref().map_or(true, |msrv| meets_msrv(self.msrv.as_ref(), msrv)) { - span_lint( + span_lint_and_help( cx, APPROX_CONSTANT, e.span, - &format!( - "approximate value of `{}::consts::{}` found. \ - Consider using it directly", - module, &name - ), + &format!("approximate value of `{}::consts::{}` found", module, &name), + None, + "consider using the constant directly", ); return; } diff --git a/tests/ui/approx_const.stderr b/tests/ui/approx_const.stderr index 98b85443f0b70..a49fd40bf337a 100644 --- a/tests/ui/approx_const.stderr +++ b/tests/ui/approx_const.stderr @@ -1,130 +1,171 @@ -error: approximate value of `f{32, 64}::consts::E` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::E` found --> $DIR/approx_const.rs:4:16 | LL | let my_e = 2.7182; | ^^^^^^ | = note: `-D clippy::approx-constant` implied by `-D warnings` + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::E` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::E` found --> $DIR/approx_const.rs:5:20 | LL | let almost_e = 2.718; | ^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::FRAC_1_PI` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::FRAC_1_PI` found --> $DIR/approx_const.rs:8:24 | LL | let my_1_frac_pi = 0.3183; | ^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::FRAC_1_SQRT_2` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::FRAC_1_SQRT_2` found --> $DIR/approx_const.rs:11:28 | LL | let my_frac_1_sqrt_2 = 0.70710678; | ^^^^^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::FRAC_1_SQRT_2` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::FRAC_1_SQRT_2` found --> $DIR/approx_const.rs:12:32 | LL | let almost_frac_1_sqrt_2 = 0.70711; | ^^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::FRAC_2_PI` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::FRAC_2_PI` found --> $DIR/approx_const.rs:15:24 | LL | let my_frac_2_pi = 0.63661977; | ^^^^^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::FRAC_2_SQRT_PI` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::FRAC_2_SQRT_PI` found --> $DIR/approx_const.rs:18:27 | LL | let my_frac_2_sq_pi = 1.128379; | ^^^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::FRAC_PI_2` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::FRAC_PI_2` found --> $DIR/approx_const.rs:21:24 | LL | let my_frac_pi_2 = 1.57079632679; | ^^^^^^^^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::FRAC_PI_3` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::FRAC_PI_3` found --> $DIR/approx_const.rs:24:24 | LL | let my_frac_pi_3 = 1.04719755119; | ^^^^^^^^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::FRAC_PI_4` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::FRAC_PI_4` found --> $DIR/approx_const.rs:27:24 | LL | let my_frac_pi_4 = 0.785398163397; | ^^^^^^^^^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::FRAC_PI_6` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::FRAC_PI_6` found --> $DIR/approx_const.rs:30:24 | LL | let my_frac_pi_6 = 0.523598775598; | ^^^^^^^^^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::FRAC_PI_8` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::FRAC_PI_8` found --> $DIR/approx_const.rs:33:24 | LL | let my_frac_pi_8 = 0.3926990816987; | ^^^^^^^^^^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::LN_10` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::LN_10` found --> $DIR/approx_const.rs:36:20 | LL | let my_ln_10 = 2.302585092994046; | ^^^^^^^^^^^^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::LN_2` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::LN_2` found --> $DIR/approx_const.rs:39:19 | LL | let my_ln_2 = 0.6931471805599453; | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::LOG10_E` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::LOG10_E` found --> $DIR/approx_const.rs:42:22 | LL | let my_log10_e = 0.4342944819032518; | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::LOG2_E` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::LOG2_E` found --> $DIR/approx_const.rs:45:21 | LL | let my_log2_e = 1.4426950408889634; | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::LOG2_10` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::LOG2_10` found --> $DIR/approx_const.rs:48:19 | LL | let log2_10 = 3.321928094887362; | ^^^^^^^^^^^^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::LOG10_2` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::LOG10_2` found --> $DIR/approx_const.rs:51:19 | LL | let log10_2 = 0.301029995663981; | ^^^^^^^^^^^^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::PI` found --> $DIR/approx_const.rs:54:17 | LL | let my_pi = 3.1415; | ^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::PI` found --> $DIR/approx_const.rs:55:21 | LL | let almost_pi = 3.14; | ^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::SQRT_2` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::SQRT_2` found --> $DIR/approx_const.rs:58:18 | LL | let my_sq2 = 1.4142; | ^^^^^^ + | + = help: consider using the constant directly error: aborting due to 21 previous errors diff --git a/tests/ui/proc_macro.stderr b/tests/ui/proc_macro.stderr index 872cbc66af622..48fd58c9a4933 100644 --- a/tests/ui/proc_macro.stderr +++ b/tests/ui/proc_macro.stderr @@ -1,10 +1,11 @@ -error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::PI` found --> $DIR/proc_macro.rs:10:14 | LL | let _x = 3.14; | ^^^^ | = note: `#[deny(clippy::approx_constant)]` on by default + = help: consider using the constant directly error: aborting due to previous error From 62b46125cb66d1f9050711d012cd16c49c99217c Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 4 Sep 2021 18:21:49 +0200 Subject: [PATCH 83/94] Avoid slice indexing in Clippy (down with the ICEs) --- clippy_lints/src/blocks_in_if_conditions.rs | 4 +-- clippy_lints/src/casts/cast_ptr_alignment.rs | 4 +-- clippy_lints/src/floating_point_arithmetic.rs | 30 +++++++++---------- clippy_lints/src/if_let_mutex.rs | 6 ++-- clippy_lints/src/if_let_some_result.rs | 4 +-- clippy_lints/src/loops/needless_range_loop.rs | 4 +-- clippy_lints/src/mem_forget.rs | 4 +-- clippy_lints/src/methods/or_fun_call.rs | 4 +-- clippy_lints/src/methods/utils.rs | 6 ++-- clippy_lints/src/misc.rs | 4 +-- clippy_lints/src/mut_mutex_lock.rs | 4 +-- clippy_lints/src/open_options.rs | 6 ++-- clippy_lints/src/ptr_offset_with_cast.rs | 8 ++--- clippy_lints/src/strings.rs | 8 ++--- clippy_lints/src/to_string_in_display.rs | 4 +-- clippy_lints/src/undropped_manually_drops.rs | 4 +-- clippy_lints/src/unused_io_amount.rs | 8 ++--- clippy_lints/src/utils/internal_lints.rs | 4 +-- 18 files changed, 58 insertions(+), 58 deletions(-) diff --git a/clippy_lints/src/blocks_in_if_conditions.rs b/clippy_lints/src/blocks_in_if_conditions.rs index 51d95cc6f0b10..47e5b0d583dab 100644 --- a/clippy_lints/src/blocks_in_if_conditions.rs +++ b/clippy_lints/src/blocks_in_if_conditions.rs @@ -61,8 +61,8 @@ impl<'a, 'tcx> Visitor<'tcx> for ExVisitor<'a, 'tcx> { // do not lint if the closure is called using an iterator (see #1141) if_chain! { if let Some(parent) = get_parent_expr(self.cx, expr); - if let ExprKind::MethodCall(_, _, args, _) = parent.kind; - let caller = self.cx.typeck_results().expr_ty(&args[0]); + if let ExprKind::MethodCall(_, _, [self_arg, ..], _) = &parent.kind; + let caller = self.cx.typeck_results().expr_ty(self_arg); if let Some(iter_id) = self.cx.tcx.get_diagnostic_item(sym::Iterator); if implements_trait(self.cx, caller, iter_id, &[]); then { diff --git a/clippy_lints/src/casts/cast_ptr_alignment.rs b/clippy_lints/src/casts/cast_ptr_alignment.rs index 62a119d662bb2..5880859e719f6 100644 --- a/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -19,7 +19,7 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { cx.typeck_results().expr_ty(expr), ); lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); - } else if let ExprKind::MethodCall(method_path, _, args, _) = expr.kind { + } else if let ExprKind::MethodCall(method_path, _, [self_arg, ..], _) = &expr.kind { if_chain! { if method_path.ident.name == sym!(cast); if let Some(generic_args) = method_path.args; @@ -28,7 +28,7 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if !is_hir_ty_cfg_dependant(cx, cast_to); then { let (cast_from, cast_to) = - (cx.typeck_results().expr_ty(&args[0]), cx.typeck_results().expr_ty(expr)); + (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr)); lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); } } diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index a643b3eb8f1b8..eda611117babf 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -362,22 +362,22 @@ fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option { if_chain! { if let ExprKind::MethodCall( PathSegment { ident: lmethod_name, .. }, - ref _lspan, - largs, + _lspan, + [largs_0, largs_1, ..], _ - ) = add_lhs.kind; + ) = &add_lhs.kind; if let ExprKind::MethodCall( PathSegment { ident: rmethod_name, .. }, - ref _rspan, - rargs, + _rspan, + [rargs_0, rargs_1, ..], _ - ) = add_rhs.kind; + ) = &add_rhs.kind; if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi"; - if let Some((lvalue, _)) = constant(cx, cx.typeck_results(), &largs[1]); - if let Some((rvalue, _)) = constant(cx, cx.typeck_results(), &rargs[1]); + if let Some((lvalue, _)) = constant(cx, cx.typeck_results(), largs_1); + if let Some((rvalue, _)) = constant(cx, cx.typeck_results(), rargs_1); if Int(2) == lvalue && Int(2) == rvalue; then { - return Some(format!("{}.hypot({})", Sugg::hir(cx, &largs[0], ".."), Sugg::hir(cx, &rargs[0], ".."))); + return Some(format!("{}.hypot({})", Sugg::hir(cx, largs_0, ".."), Sugg::hir(cx, rargs_0, ".."))); } } } @@ -407,8 +407,8 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { if cx.typeck_results().expr_ty(lhs).is_floating_point(); if let Some((value, _)) = constant(cx, cx.typeck_results(), rhs); if F32(1.0) == value || F64(1.0) == value; - if let ExprKind::MethodCall(path, _, method_args, _) = lhs.kind; - if cx.typeck_results().expr_ty(&method_args[0]).is_floating_point(); + if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &lhs.kind; + if cx.typeck_results().expr_ty(self_arg).is_floating_point(); if path.ident.name.as_str() == "exp"; then { span_lint_and_sugg( @@ -419,7 +419,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { "consider using", format!( "{}.exp_m1()", - Sugg::hir(cx, &method_args[0], "..") + Sugg::hir(cx, self_arg, "..") ), Applicability::MachineApplicable, ); @@ -617,8 +617,8 @@ fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) { rhs, ) = &expr.kind; if are_same_base_logs(cx, lhs, rhs); - if let ExprKind::MethodCall(_, _, largs, _) = lhs.kind; - if let ExprKind::MethodCall(_, _, rargs, _) = rhs.kind; + if let ExprKind::MethodCall(_, _, [largs_self, ..], _) = &lhs.kind; + if let ExprKind::MethodCall(_, _, [rargs_self, ..], _) = &rhs.kind; then { span_lint_and_sugg( cx, @@ -626,7 +626,7 @@ fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) { expr.span, "log base can be expressed more clearly", "consider using", - format!("{}.log({})", Sugg::hir(cx, &largs[0], ".."), Sugg::hir(cx, &rargs[0], ".."),), + format!("{}.log({})", Sugg::hir(cx, largs_self, ".."), Sugg::hir(cx, rargs_self, ".."),), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs index 7dad1c31150e2..ef72b88b3c773 100644 --- a/clippy_lints/src/if_let_mutex.rs +++ b/clippy_lints/src/if_let_mutex.rs @@ -138,12 +138,12 @@ impl<'tcx, 'l> ArmVisitor<'tcx, 'l> { fn is_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { if_chain! { - if let ExprKind::MethodCall(path, _span, args, _) = &expr.kind; + if let ExprKind::MethodCall(path, _span, [self_arg, ..], _) = &expr.kind; if path.ident.as_str() == "lock"; - let ty = cx.typeck_results().expr_ty(&args[0]); + let ty = cx.typeck_results().expr_ty(self_arg); if is_type_diagnostic_item(cx, ty, sym!(mutex_type)); then { - Some(&args[0]) + Some(self_arg) } else { None } diff --git a/clippy_lints/src/if_let_some_result.rs b/clippy_lints/src/if_let_some_result.rs index 33eba02dae063..adcd78ed0d427 100644 --- a/clippy_lints/src/if_let_some_result.rs +++ b/clippy_lints/src/if_let_some_result.rs @@ -46,10 +46,10 @@ impl<'tcx> LateLintPass<'tcx> for OkIfLet { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { //begin checking variables if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(cx, expr); - if let ExprKind::MethodCall(_, ok_span, result_types, _) = let_expr.kind; //check is expr.ok() has type Result.ok(, _) + if let ExprKind::MethodCall(_, ok_span, [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 is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&result_types[0]), sym::result_type); + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(result_types_0), sym::result_type); if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some"; then { diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index f17e76d9682c9..e8f3550283a49 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -301,10 +301,10 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { if_chain! { // a range index op - if let ExprKind::MethodCall(meth, _, args, _) = expr.kind; + if let ExprKind::MethodCall(meth, _, [args_0, args_1, ..], _) = &expr.kind; if (meth.ident.name == sym::index && match_trait_method(self.cx, expr, &paths::INDEX)) || (meth.ident.name == sym::index_mut && match_trait_method(self.cx, expr, &paths::INDEX_MUT)); - if !self.check(&args[1], &args[0], expr); + if !self.check(args_1, args_0, expr); then { return } } diff --git a/clippy_lints/src/mem_forget.rs b/clippy_lints/src/mem_forget.rs index 07202a59c4b96..eb437dc47afb4 100644 --- a/clippy_lints/src/mem_forget.rs +++ b/clippy_lints/src/mem_forget.rs @@ -28,11 +28,11 @@ declare_lint_pass!(MemForget => [MEM_FORGET]); impl<'tcx> LateLintPass<'tcx> for MemForget { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let ExprKind::Call(path_expr, args) = e.kind { + if let ExprKind::Call(path_expr, [ref first_arg, ..]) = e.kind { if let ExprKind::Path(ref qpath) = path_expr.kind { if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id() { if match_def_path(cx, def_id, &paths::MEM_FORGET) { - let forgot_ty = cx.typeck_results().expr_ty(&args[0]); + let forgot_ty = cx.typeck_results().expr_ty(first_arg); if forgot_ty.ty_adt_def().map_or(false, |def| def.has_dtor(cx.tcx)) { span_lint(cx, MEM_FORGET, e.span, "usage of `mem::forget` on `Drop` type"); diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index c1d22e5d72c13..30ed1d665a907 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -96,9 +96,9 @@ pub(super) fn check<'tcx>( (&paths::RESULT, true, &["or", "unwrap_or"], "else"), ]; - if let hir::ExprKind::MethodCall(path, _, args, _) = &arg.kind { + if let hir::ExprKind::MethodCall(path, _, [self_arg, ..], _) = &arg.kind { if path.ident.name == sym::len { - let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); + let ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); match ty.kind() { ty::Slice(_) | ty::Array(_, _) | ty::Str => return, diff --git a/clippy_lints/src/methods/utils.rs b/clippy_lints/src/methods/utils.rs index 0daea47816a51..30d6665a920ba 100644 --- a/clippy_lints/src/methods/utils.rs +++ b/clippy_lints/src/methods/utils.rs @@ -24,9 +24,9 @@ pub(super) fn derefs_to_slice<'tcx>( } } - if let hir::ExprKind::MethodCall(path, _, args, _) = expr.kind { - if path.ident.name == sym::iter && may_slice(cx, cx.typeck_results().expr_ty(&args[0])) { - Some(&args[0]) + if let hir::ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind { + if path.ident.name == sym::iter && may_slice(cx, cx.typeck_results().expr_ty(self_arg)) { + Some(self_arg) } else { None } diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index c796abe9815a4..538fa4e1678fc 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -513,12 +513,12 @@ fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } if_chain! { - if let ExprKind::MethodCall(method_name, _, expressions, _) = expr.kind; + if let ExprKind::MethodCall(method_name, _, [ref self_arg, ..], _) = expr.kind; if sym!(signum) == method_name.ident.name; // Check that the receiver of the signum() is a float (expressions[0] is the receiver of // the method call) then { - return is_float(cx, &expressions[0]); + return is_float(cx, self_arg); } } false diff --git a/clippy_lints/src/mut_mutex_lock.rs b/clippy_lints/src/mut_mutex_lock.rs index 85e870632a5ca..e9dcc7b227d76 100644 --- a/clippy_lints/src/mut_mutex_lock.rs +++ b/clippy_lints/src/mut_mutex_lock.rs @@ -47,9 +47,9 @@ declare_lint_pass!(MutMutexLock => [MUT_MUTEX_LOCK]); impl<'tcx> LateLintPass<'tcx> for MutMutexLock { fn check_expr(&mut self, cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>) { if_chain! { - if let ExprKind::MethodCall(path, method_span, args, _) = &ex.kind; + if let ExprKind::MethodCall(path, method_span, [self_arg, ..], _) = &ex.kind; if path.ident.name == sym!(lock); - let ty = cx.typeck_results().expr_ty(&args[0]); + let ty = cx.typeck_results().expr_ty(self_arg); if let ty::Ref(_, inner_ty, Mutability::Mut) = ty.kind(); if is_type_diagnostic_item(cx, inner_ty, sym!(mutex_type)); then { diff --git a/clippy_lints/src/open_options.rs b/clippy_lints/src/open_options.rs index 4064d94da2abf..5752342cf623b 100644 --- a/clippy_lints/src/open_options.rs +++ b/clippy_lints/src/open_options.rs @@ -31,11 +31,11 @@ declare_lint_pass!(OpenOptions => [NONSENSICAL_OPEN_OPTIONS]); impl<'tcx> LateLintPass<'tcx> for OpenOptions { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let ExprKind::MethodCall(path, _, arguments, _) = e.kind { - let obj_ty = cx.typeck_results().expr_ty(&arguments[0]).peel_refs(); + if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &e.kind { + let obj_ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); if path.ident.name == sym!(open) && match_type(cx, obj_ty, &paths::OPEN_OPTIONS) { let mut options = Vec::new(); - get_open_options(cx, &arguments[0], &mut options); + get_open_options(cx, self_arg, &mut options); check_open_options(cx, &options, e.span); } } diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs index f1975056ddc9b..cfb5287c6673f 100644 --- a/clippy_lints/src/ptr_offset_with_cast.rs +++ b/clippy_lints/src/ptr_offset_with_cast.rs @@ -92,13 +92,13 @@ fn expr_as_ptr_offset_call<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Method)> { - if let ExprKind::MethodCall(path_segment, _, args, _) = expr.kind { - if is_expr_ty_raw_ptr(cx, &args[0]) { + if let ExprKind::MethodCall(path_segment, _, [arg_0, arg_1, ..], _) = &expr.kind { + if is_expr_ty_raw_ptr(cx, arg_0) { if path_segment.ident.name == sym::offset { - return Some((&args[0], &args[1], Method::Offset)); + return Some((arg_0, arg_1, Method::Offset)); } if path_segment.ident.name == sym!(wrapping_offset) { - return Some((&args[0], &args[1], Method::WrappingOffset)); + return Some((arg_0, arg_1, Method::WrappingOffset)); } } } diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 1a78a4968e5a3..13d8f954c4413 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -345,9 +345,9 @@ declare_lint_pass!(StrToString => [STR_TO_STRING]); impl LateLintPass<'_> for StrToString { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { if_chain! { - if let ExprKind::MethodCall(path, _, args, _) = &expr.kind; + if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind; if path.ident.name == sym!(to_string); - let ty = cx.typeck_results().expr_ty(&args[0]); + let ty = cx.typeck_results().expr_ty(self_arg); if let ty::Ref(_, ty, ..) = ty.kind(); if *ty.kind() == ty::Str; then { @@ -394,9 +394,9 @@ declare_lint_pass!(StringToString => [STRING_TO_STRING]); impl LateLintPass<'_> for StringToString { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { if_chain! { - if let ExprKind::MethodCall(path, _, args, _) = &expr.kind; + if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind; if path.ident.name == sym!(to_string); - let ty = cx.typeck_results().expr_ty(&args[0]); + let ty = cx.typeck_results().expr_ty(self_arg); if is_type_diagnostic_item(cx, ty, sym::string_type); then { span_lint_and_help( diff --git a/clippy_lints/src/to_string_in_display.rs b/clippy_lints/src/to_string_in_display.rs index b036ed9a3d2e7..b7414cec87cd2 100644 --- a/clippy_lints/src/to_string_in_display.rs +++ b/clippy_lints/src/to_string_in_display.rs @@ -92,11 +92,11 @@ impl LateLintPass<'_> for ToStringInDisplay { if_chain! { if self.in_display_impl; if let Some(self_hir_id) = self.self_hir_id; - if let ExprKind::MethodCall(path, _, args, _) = expr.kind; + if let ExprKind::MethodCall(path, _, [ref self_arg, ..], _) = expr.kind; if path.ident.name == sym!(to_string); if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if is_diag_trait_item(cx, expr_def_id, sym::ToString); - if path_to_local_id(&args[0], self_hir_id); + if path_to_local_id(self_arg, self_hir_id); then { span_lint( cx, diff --git a/clippy_lints/src/undropped_manually_drops.rs b/clippy_lints/src/undropped_manually_drops.rs index 47571e608c780..0957061659362 100644 --- a/clippy_lints/src/undropped_manually_drops.rs +++ b/clippy_lints/src/undropped_manually_drops.rs @@ -37,8 +37,8 @@ declare_lint_pass!(UndroppedManuallyDrops => [UNDROPPED_MANUALLY_DROPS]); impl LateLintPass<'tcx> for UndroppedManuallyDrops { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let Some(args) = match_function_call(cx, expr, &paths::DROP) { - let ty = cx.typeck_results().expr_ty(&args[0]); + if let Some([arg_0, ..]) = match_function_call(cx, expr, &paths::DROP) { + let ty = cx.typeck_results().expr_ty(arg_0); if is_type_lang_item(cx, ty, lang_items::LangItem::ManuallyDrop) { span_lint_and_help( cx, diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 82bc4a6d15343..031b182bd2fa0 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -45,20 +45,20 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { match expr.kind { hir::ExprKind::Match(res, _, _) if is_try(cx, expr).is_some() => { - if let hir::ExprKind::Call(func, args) = res.kind { + if let hir::ExprKind::Call(func, [ref arg_0, ..]) = res.kind { if matches!( func.kind, hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryTraitBranch, _)) ) { - check_map_error(cx, &args[0], expr); + check_map_error(cx, arg_0, expr); } } else { check_map_error(cx, res, expr); } }, - hir::ExprKind::MethodCall(path, _, args, _) => match &*path.ident.as_str() { + hir::ExprKind::MethodCall(path, _, [ref arg_0, ..], _) => match &*path.ident.as_str() { "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" => { - check_map_error(cx, &args[0], expr); + check_map_error(cx, arg_0, expr); }, _ => (), }, diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 42d51272279e2..756c33d70c26d 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -504,10 +504,10 @@ impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions { } if_chain! { - if let ExprKind::MethodCall(path, _, args, _) = expr.kind; + if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind; let fn_name = path.ident; if let Some(sugg) = self.map.get(&*fn_name.as_str()); - let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); + let ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); if match_type(cx, ty, &paths::EARLY_CONTEXT) || match_type(cx, ty, &paths::LATE_CONTEXT); then { From a7a5a5d9b00e6ecc0e518b1e806c1f3e2d216502 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Tue, 7 Sep 2021 05:50:04 +0200 Subject: [PATCH 84/94] Add `TAU` to `approx_constant` --- clippy_lints/src/approx_const.rs | 3 ++- clippy_utils/src/msrvs.rs | 1 + tests/ui/approx_const.rs | 4 ++++ tests/ui/approx_const.stderr | 18 +++++++++++++++++- 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs index a57d8b67ed37d..fb54ac1ec5119 100644 --- a/clippy_lints/src/approx_const.rs +++ b/clippy_lints/src/approx_const.rs @@ -39,7 +39,7 @@ declare_clippy_lint! { } // Tuples are of the form (constant, name, min_digits, msrv) -const KNOWN_CONSTS: [(f64, &str, usize, Option); 18] = [ +const KNOWN_CONSTS: [(f64, &str, usize, Option); 19] = [ (f64::E, "E", 4, None), (f64::FRAC_1_PI, "FRAC_1_PI", 4, None), (f64::FRAC_1_SQRT_2, "FRAC_1_SQRT_2", 5, None), @@ -58,6 +58,7 @@ const KNOWN_CONSTS: [(f64, &str, usize, Option); 18] = [ (f64::LOG10_E, "LOG10_E", 5, None), (f64::PI, "PI", 3, None), (f64::SQRT_2, "SQRT_2", 5, None), + (f64::TAU, "TAU", 3, Some(msrvs::TAU)), ]; pub struct ApproxConstant { diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 6fab17f07fe0b..fa57dfbb57edc 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -15,6 +15,7 @@ msrv_aliases! { 1,53,0 { OR_PATTERNS } 1,52,0 { STR_SPLIT_ONCE } 1,50,0 { BOOL_THEN } + 1,47,0 { TAU } 1,46,0 { CONST_IF_MATCH } 1,45,0 { STR_STRIP_PREFIX } 1,43,0 { LOG2_10, LOG10_2 } diff --git a/tests/ui/approx_const.rs b/tests/ui/approx_const.rs index fb57a0becbb25..2ae4d613507ed 100644 --- a/tests/ui/approx_const.rs +++ b/tests/ui/approx_const.rs @@ -57,4 +57,8 @@ fn main() { let my_sq2 = 1.4142; let no_sq2 = 1.414; + + let my_tau = 6.2832; + let almost_tau = 6.28; + let no_tau = 6.3; } diff --git a/tests/ui/approx_const.stderr b/tests/ui/approx_const.stderr index a49fd40bf337a..4da1b8215ae04 100644 --- a/tests/ui/approx_const.stderr +++ b/tests/ui/approx_const.stderr @@ -167,5 +167,21 @@ LL | let my_sq2 = 1.4142; | = help: consider using the constant directly -error: aborting due to 21 previous errors +error: approximate value of `f{32, 64}::consts::TAU` found + --> $DIR/approx_const.rs:61:18 + | +LL | let my_tau = 6.2832; + | ^^^^^^ + | + = help: consider using the constant directly + +error: approximate value of `f{32, 64}::consts::TAU` found + --> $DIR/approx_const.rs:62:22 + | +LL | let almost_tau = 6.28; + | ^^^^ + | + = help: consider using the constant directly + +error: aborting due to 23 previous errors From 290fb8de6637262c79bda37723bf784d6341056b Mon Sep 17 00:00:00 2001 From: dswij Date: Thu, 26 Aug 2021 18:20:13 +0800 Subject: [PATCH 85/94] Add additional test for broken loop in `mut_range_bound` --- tests/ui/mut_range_bound.rs | 39 ++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/tests/ui/mut_range_bound.rs b/tests/ui/mut_range_bound.rs index 1348dd2a3d8bb..e1ae1ef928223 100644 --- a/tests/ui/mut_range_bound.rs +++ b/tests/ui/mut_range_bound.rs @@ -1,14 +1,6 @@ #![allow(unused)] -fn main() { - mut_range_bound_upper(); - mut_range_bound_lower(); - mut_range_bound_both(); - mut_range_bound_no_mutation(); - immut_range_bound(); - mut_borrow_range_bound(); - immut_borrow_range_bound(); -} +fn main() {} fn mut_range_bound_upper() { let mut m = 4; @@ -61,3 +53,32 @@ fn immut_range_bound() { continue; } // no warning } + +fn mut_range_bound_break() { + let mut m = 4; + for i in 0..m { + if m == 4 { + m = 5; // no warning because of immediate break + break; + } + } +} + +fn mut_range_bound_no_immediate_break() { + let mut m = 4; + for i in 0..m { + m = 2; // warning because it is not immediately followed by break + if m == 4 { + break; + } + } + + let mut n = 3; + for i in n..10 { + if n == 4 { + n = 1; // FIXME: warning because is is not immediately followed by break + let _ = 2; + break; + } + } +} From 8fbf75e0f9f7abf0f7820f1a89dfc230d3bc918f Mon Sep 17 00:00:00 2001 From: dswij Date: Thu, 2 Sep 2021 16:04:46 +0800 Subject: [PATCH 86/94] `mut_range_bound` to check for immediate break from loop --- clippy_lints/src/loops/mut_range_bound.rs | 98 ++++++++++++++++++----- 1 file changed, 77 insertions(+), 21 deletions(-) diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index 344dc5074d369..358d53e8859d0 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -1,24 +1,27 @@ use super::MUT_RANGE_BOUND; -use clippy_utils::diagnostics::span_lint; -use clippy_utils::{higher, path_to_local}; +use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::{get_enclosing_block, higher, path_to_local}; use if_chain::if_chain; -use rustc_hir::{BindingAnnotation, Expr, HirId, Node, PatKind}; +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, Node, PatKind}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; +use rustc_middle::hir::map::Map; use rustc_middle::{mir::FakeReadCause, ty}; use rustc_span::source_map::Span; use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) { - if let Some(higher::Range { - start: Some(start), - end: Some(end), - .. - }) = higher::Range::hir(arg) - { - let mut_ids = vec![check_for_mutability(cx, start), check_for_mutability(cx, end)]; - if mut_ids[0].is_some() || mut_ids[1].is_some() { - let (span_low, span_high) = check_for_mutation(cx, body, &mut_ids); + if_chain! { + if let Some(higher::Range { + start: Some(start), + end: Some(end), + .. + }) = higher::Range::hir(arg); + let (mut_id_start, mut_id_end) = (check_for_mutability(cx, start), check_for_mutability(cx, end)); + if mut_id_start.is_some() || mut_id_end.is_some(); + then { + let (span_low, span_high) = check_for_mutation(cx, body, mut_id_start, mut_id_end); mut_warn_with_span(cx, span_low); mut_warn_with_span(cx, span_high); } @@ -27,11 +30,13 @@ pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) { fn mut_warn_with_span(cx: &LateContext<'_>, span: Option) { if let Some(sp) = span { - span_lint( + span_lint_and_note( cx, MUT_RANGE_BOUND, sp, - "attempt to mutate range bound within loop; note that the range of the loop is unchanged", + "attempt to mutate range bound within loop", + None, + "the range of the loop is unchanged", ); } } @@ -51,12 +56,13 @@ fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option fn check_for_mutation<'tcx>( cx: &LateContext<'tcx>, body: &Expr<'_>, - bound_ids: &[Option], + bound_id_start: Option, + bound_id_end: Option, ) -> (Option, Option) { let mut delegate = MutatePairDelegate { cx, - hir_id_low: bound_ids[0], - hir_id_high: bound_ids[1], + hir_id_low: bound_id_start, + hir_id_high: bound_id_end, span_low: None, span_high: None, }; @@ -70,6 +76,7 @@ fn check_for_mutation<'tcx>( ) .walk_expr(body); }); + delegate.mutation_span() } @@ -87,10 +94,10 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> { fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) { if let ty::BorrowKind::MutBorrow = bk { if let PlaceBase::Local(id) = cmt.place.base { - if Some(id) == self.hir_id_low { + if Some(id) == self.hir_id_low && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id)); } - if Some(id) == self.hir_id_high { + if Some(id) == self.hir_id_high && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id)); } } @@ -99,10 +106,10 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> { fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) { if let PlaceBase::Local(id) = cmt.place.base { - if Some(id) == self.hir_id_low { + if Some(id) == self.hir_id_low && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id)); } - if Some(id) == self.hir_id_high { + if Some(id) == self.hir_id_high && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id)); } } @@ -116,3 +123,52 @@ impl MutatePairDelegate<'_, '_> { (self.span_low, self.span_high) } } + +struct BreakAfterExprVisitor { + hir_id: HirId, + past_expr: bool, + past_candidate: bool, + break_after_expr: bool, +} + +impl BreakAfterExprVisitor { + pub fn is_found(cx: &LateContext<'_>, hir_id: HirId) -> bool { + let mut visitor = BreakAfterExprVisitor { + hir_id, + past_expr: false, + past_candidate: false, + break_after_expr: false, + }; + + get_enclosing_block(cx, hir_id).map_or(false, |block| { + visitor.visit_block(block); + visitor.break_after_expr + }) + } +} + +impl intravisit::Visitor<'tcx> for BreakAfterExprVisitor { + type Map = Map<'tcx>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + if self.past_candidate { + return; + } + + if expr.hir_id == self.hir_id { + self.past_expr = true; + } else if self.past_expr { + if matches!(&expr.kind, ExprKind::Break(..)) { + self.break_after_expr = true; + } + + self.past_candidate = true; + } else { + intravisit::walk_expr(self, expr); + } + } +} From 7515d9c6f77a3325e6142c45ebe1bc78f580b894 Mon Sep 17 00:00:00 2001 From: dswij Date: Fri, 3 Sep 2021 12:27:05 +0800 Subject: [PATCH 87/94] Update test output for `mut_range_bound` --- tests/ui/mut_range_bound.stderr | 47 +++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/tests/ui/mut_range_bound.stderr b/tests/ui/mut_range_bound.stderr index 0eeb76e0ec5fd..4b5a3fc1e418c 100644 --- a/tests/ui/mut_range_bound.stderr +++ b/tests/ui/mut_range_bound.stderr @@ -1,34 +1,59 @@ -error: attempt to mutate range bound within loop; note that the range of the loop is unchanged - --> $DIR/mut_range_bound.rs:16:9 +error: attempt to mutate range bound within loop + --> $DIR/mut_range_bound.rs:8:9 | LL | m = 5; | ^ | = note: `-D clippy::mut-range-bound` implied by `-D warnings` + = note: the range of the loop is unchanged -error: attempt to mutate range bound within loop; note that the range of the loop is unchanged - --> $DIR/mut_range_bound.rs:23:9 +error: attempt to mutate range bound within loop + --> $DIR/mut_range_bound.rs:15:9 | LL | m *= 2; | ^ + | + = note: the range of the loop is unchanged -error: attempt to mutate range bound within loop; note that the range of the loop is unchanged - --> $DIR/mut_range_bound.rs:31:9 +error: attempt to mutate range bound within loop + --> $DIR/mut_range_bound.rs:23:9 | LL | m = 5; | ^ + | + = note: the range of the loop is unchanged -error: attempt to mutate range bound within loop; note that the range of the loop is unchanged - --> $DIR/mut_range_bound.rs:32:9 +error: attempt to mutate range bound within loop + --> $DIR/mut_range_bound.rs:24:9 | LL | n = 7; | ^ + | + = note: the range of the loop is unchanged -error: attempt to mutate range bound within loop; note that the range of the loop is unchanged - --> $DIR/mut_range_bound.rs:46:22 +error: attempt to mutate range bound within loop + --> $DIR/mut_range_bound.rs:38:22 | LL | let n = &mut m; // warning | ^ + | + = note: the range of the loop is unchanged + +error: attempt to mutate range bound within loop + --> $DIR/mut_range_bound.rs:70:9 + | +LL | m = 2; // warning because it is not immediately followed by break + | ^ + | + = note: the range of the loop is unchanged + +error: attempt to mutate range bound within loop + --> $DIR/mut_range_bound.rs:79:13 + | +LL | n = 1; // FIXME: warning because is is not immediately followed by break + | ^ + | + = note: the range of the loop is unchanged -error: aborting due to 5 previous errors +error: aborting due to 7 previous errors From dc6f7dc6bf13295d5249c7f2a9aa71024b6555ad Mon Sep 17 00:00:00 2001 From: dswij Date: Tue, 7 Sep 2021 06:07:50 +0800 Subject: [PATCH 88/94] Add known problems to `mut_range_bound` docs --- clippy_lints/src/loops/mod.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 97cbe2c53ddac..2860cb68f42f2 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -397,6 +397,21 @@ declare_clippy_lint! { /// ### Why is this bad? /// One might think that modifying the mutable variable changes the loop bounds /// + /// ### Known problems + /// False positive when mutation is followed by a `break`, but the `break` is not immediately + /// after the mutation: + /// + /// ```rust + /// let mut x = 5; + /// for _ in 0..x { + /// x += 1; // x is a range bound that is mutated + /// ..; // some other expression + /// break; // leaves the loop, so mutation is not an issue + /// } + /// ``` + /// + /// False positive on nested loops ([#6072](https://github.com/rust-lang/rust-clippy/issues/6072)) + /// /// ### Example /// ```rust /// let mut foo = 42; From 3cb1d283d64bd2061c151820202985fa08c2787e Mon Sep 17 00:00:00 2001 From: flip1995 Date: Wed, 8 Sep 2021 10:58:51 +0200 Subject: [PATCH 89/94] Bump nightly version -> 2021-09-08 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 8b0563eda6d1d..92bde3423a20a 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-09-02" +channel = "nightly-2021-09-08" components = ["llvm-tools-preview", "rustc-dev", "rust-src"] From 563a00b2a0370d4594f8a20b409323da576c5d84 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Wed, 8 Sep 2021 10:59:04 +0200 Subject: [PATCH 90/94] Bump Clippy Version -> 0.1.57 --- Cargo.toml | 2 +- clippy_lints/Cargo.toml | 4 +--- clippy_utils/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 16b6c207a5f19..20327573db7fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.56" +version = "0.1.57" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index a3b92e1faa1a9..3c28024bf926a 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,8 +1,6 @@ [package] name = "clippy_lints" -# begin automatic update -version = "0.1.56" -# end automatic update +version = "0.1.57" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index c65b2958ec56d..ffc89be0ae557 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.56" +version = "0.1.57" edition = "2018" publish = false From ffe21e58a07816fd1e2492abce2620113a7dc6e3 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 3 Sep 2021 15:55:14 -0500 Subject: [PATCH 91/94] Remove unused dependencies --- clippy_utils/Cargo.toml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index ffc89be0ae557..4c038a997952a 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -6,10 +6,6 @@ publish = false [dependencies] if_chain = "1.0.0" -itertools = "0.9" -regex-syntax = "0.6" -serde = { version = "1.0", features = ["derive"] } -unicode-normalization = "0.1" rustc-semver="1.1.0" [features] From 8c05a15f0a5b541a5eb29f88f2d6dffa5888a2ce Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 3 Sep 2021 15:24:29 -0500 Subject: [PATCH 92/94] Use binary-dep-depinfo to resolve UI dependencies --- .cargo/config | 3 +- Cargo.toml | 13 ++-- tests/compile-test.rs | 152 +++++++++++++++++++++--------------------- 3 files changed, 88 insertions(+), 80 deletions(-) diff --git a/.cargo/config b/.cargo/config index e95ea224cb681..84ae36a46d71d 100644 --- a/.cargo/config +++ b/.cargo/config @@ -5,4 +5,5 @@ lintcheck = "run --target-dir lintcheck/target --package lintcheck --bin lintche collect-metadata = "test --test dogfood --features metadata-collector-lint -- run_metadata_collection_lint --ignored" [build] -rustflags = ["-Zunstable-options"] +# -Zbinary-dep-depinfo allows us to track which rlib files to use for compiling UI tests +rustflags = ["-Zunstable-options", "-Zbinary-dep-depinfo"] diff --git a/Cargo.toml b/Cargo.toml index 20327573db7fc..2310370fb9fbe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,11 +32,7 @@ tempfile = { version = "3.1.0", optional = true } cargo_metadata = "0.12" compiletest_rs = { version = "0.6.0", features = ["tmp"] } tester = "0.9" -serde = { version = "1.0", features = ["derive"] } -derive-new = "0.5" regex = "1.4" -quote = "1" -syn = { version = "1", features = ["full"] } # This is used by the `collect-metadata` alias. filetime = "0.2" @@ -45,6 +41,15 @@ filetime = "0.2" # for more information. rustc-workspace-hack = "1.0.0" +# UI test dependencies +clippy_utils = { path = "clippy_utils" } +derive-new = "0.5" +if_chain = "1.0" +itertools = "0.10.1" +quote = "1" +serde = { version = "1.0", features = ["derive"] } +syn = { version = "1", features = ["full"] } + [build-dependencies] rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util" } diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 6116acffe07f4..74fe6f548a1b6 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -1,10 +1,10 @@ #![feature(test)] // compiletest_rs requires this attribute #![feature(once_cell)] -#![feature(try_blocks)] use compiletest_rs as compiletest; use compiletest_rs::common::Mode as TestMode; +use std::collections::HashMap; use std::env::{self, remove_var, set_var, var_os}; use std::ffi::{OsStr, OsString}; use std::fs; @@ -16,6 +16,28 @@ mod cargo; // whether to run internal tests or not const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal-lints"); +/// All crates used in UI tests are listed here +static TEST_DEPENDENCIES: &[&str] = &[ + "clippy_utils", + "derive_new", + "if_chain", + "itertools", + "quote", + "regex", + "serde", + "serde_derive", + "syn", +]; + +// Test dependencies may need an `extern crate` here to ensure that they show up +// in the depinfo file (otherwise cargo thinks they are unused) +extern crate clippy_utils; +extern crate derive_new; +extern crate if_chain; +extern crate itertools; +extern crate quote; +extern crate syn; + fn host_lib() -> PathBuf { option_env!("HOST_LIBS").map_or(cargo::CARGO_TARGET_DIR.join(env!("PROFILE")), PathBuf::from) } @@ -24,72 +46,58 @@ fn clippy_driver_path() -> PathBuf { option_env!("CLIPPY_DRIVER_PATH").map_or(cargo::TARGET_LIB.join("clippy-driver"), PathBuf::from) } -// When we'll want to use `extern crate ..` for a dependency that is used -// both by the crate and the compiler itself, we can't simply pass -L flags -// as we'll get a duplicate matching versions. Instead, disambiguate with -// `--extern dep=path`. -// See https://github.com/rust-lang/rust-clippy/issues/4015. -// -// FIXME: We cannot use `cargo build --message-format=json` to resolve to dependency files. -// Because it would force-rebuild if the options passed to `build` command is not the same -// as what we manually pass to `cargo` invocation -fn third_party_crates() -> String { - use std::collections::HashMap; - static CRATES: &[&str] = &[ - "clippy_lints", - "clippy_utils", - "if_chain", - "itertools", - "quote", - "regex", - "serde", - "serde_derive", - "syn", - ]; - let dep_dir = cargo::TARGET_LIB.join("deps"); - let mut crates: HashMap<&str, Vec> = HashMap::with_capacity(CRATES.len()); - let mut flags = String::new(); - for entry in fs::read_dir(dep_dir).unwrap().flatten() { - let path = entry.path(); - if let Some(name) = try { - let name = path.file_name()?.to_str()?; - let (name, _) = name.strip_suffix(".rlib")?.strip_prefix("lib")?.split_once('-')?; - CRATES.iter().copied().find(|&c| c == name)? - } { - flags += &format!(" --extern {}={}", name, path.display()); - crates.entry(name).or_default().push(path.clone()); +/// Produces a string with an `--extern` flag for all UI test crate +/// dependencies. +/// +/// The dependency files are located by parsing the depinfo file for this test +/// module. This assumes the `-Z binary-dep-depinfo` flag is enabled. All test +/// dependencies must be added to Cargo.toml at the project root. Test +/// dependencies that are not *directly* used by this test module require an +/// `extern crate` declaration. +fn extern_flags() -> String { + let current_exe_depinfo = { + let mut path = env::current_exe().unwrap(); + path.set_extension("d"); + std::fs::read_to_string(path).unwrap() + }; + let mut crates: HashMap<&str, &str> = HashMap::with_capacity(TEST_DEPENDENCIES.len()); + for line in current_exe_depinfo.lines() { + // each dependency is expected to have a Makefile rule like `/path/to/crate-hash.rlib:` + let parse_name_path = || { + if line.starts_with(char::is_whitespace) { + return None; + } + let path_str = line.strip_suffix(':')?; + let path = Path::new(path_str); + if !matches!(path.extension()?.to_str()?, "rlib" | "so" | "dylib" | "dll") { + return None; + } + let (name, _hash) = path.file_stem()?.to_str()?.rsplit_once('-')?; + // the "lib" prefix is not present for dll files + let name = name.strip_prefix("lib").unwrap_or(name); + Some((name, path_str)) + }; + if let Some((name, path)) = parse_name_path() { + if TEST_DEPENDENCIES.contains(&name) { + // A dependency may be listed twice if it is available in sysroot, + // and the sysroot dependencies are listed first. As of the writing, + // this only seems to apply to if_chain. + crates.insert(name, path); + } } } - crates.retain(|_, paths| paths.len() > 1); - if !crates.is_empty() { - let crate_names = crates.keys().map(|s| format!("`{}`", s)).collect::>().join(", "); - // add backslashes for an easy copy-paste `rm` command - let paths = crates - .into_values() - .flatten() - .map(|p| strip_current_dir(&p).display().to_string()) - .collect::>() - .join(" \\\n"); - // Check which action should be done in order to remove compiled deps. - // If pre-installed version of compiler is used, `cargo clean` will do. - // Otherwise (for bootstrapped compiler), the dependencies directory - // must be removed manually. - let suggested_action = if std::env::var_os("RUSTC_BOOTSTRAP").is_some() { - "removing the stageN-tools directory" - } else { - "running `cargo clean`" - }; - - panic!( - "\n----------------------------------------------------------------------\n\ - ERROR: Found multiple rlibs for crates: {}\n\ - Try {} or remove the following files:\n\n{}\n\n\ - For details on this error see https://github.com/rust-lang/rust-clippy/issues/7343\n\ - ----------------------------------------------------------------------\n", - crate_names, suggested_action, paths - ); + let not_found: Vec<&str> = TEST_DEPENDENCIES + .iter() + .copied() + .filter(|n| !crates.contains_key(n)) + .collect(); + if !not_found.is_empty() { + panic!("dependencies not found in depinfo: {:?}", not_found); } - flags + crates + .into_iter() + .map(|(name, path)| format!("--extern {}={} ", name, path)) + .collect() } fn default_config() -> compiletest::Config { @@ -105,11 +113,14 @@ fn default_config() -> compiletest::Config { config.compile_lib_path = path; } + // Using `-L dependency={}` enforces that external dependencies are added with `--extern`. + // This is valuable because a) it allows us to monitor what external dependencies are used + // and b) it ensures that conflicting rlibs are resolved properly. config.target_rustcflags = Some(format!( - "--emit=metadata -L {0} -L {1} -Dwarnings -Zui-testing {2}", + "--emit=metadata -L dependency={} -L dependency={} -Dwarnings -Zui-testing {}", host_lib().join("deps").display(), cargo::TARGET_LIB.join("deps").display(), - third_party_crates(), + extern_flags(), )); config.build_base = host_lib().join("test_build_base"); @@ -316,12 +327,3 @@ impl Drop for VarGuard { } } } - -fn strip_current_dir(path: &Path) -> &Path { - if let Ok(curr) = env::current_dir() { - if let Ok(stripped) = path.strip_prefix(curr) { - return stripped; - } - } - path -} From 5d3fc6fc08d604889e5c52ad8b38ea474fab9a6f Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 8 Sep 2021 08:19:14 -0500 Subject: [PATCH 93/94] Deny warnings in test modules --- tests/compile-test.rs | 8 ++++++++ tests/dogfood.rs | 2 ++ tests/fmt.rs | 3 +++ tests/integration.rs | 2 ++ tests/lint_message_convention.rs | 3 +++ tests/missing-test-files.rs | 2 ++ tests/versioncheck.rs | 3 +++ 7 files changed, 23 insertions(+) diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 74fe6f548a1b6..c63c47690d52a 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -1,5 +1,7 @@ #![feature(test)] // compiletest_rs requires this attribute #![feature(once_cell)] +#![cfg_attr(feature = "deny-warnings", deny(warnings))] +#![warn(rust_2018_idioms, unused_lifetimes)] use compiletest_rs as compiletest; use compiletest_rs::common::Mode as TestMode; @@ -31,11 +33,17 @@ static TEST_DEPENDENCIES: &[&str] = &[ // Test dependencies may need an `extern crate` here to ensure that they show up // in the depinfo file (otherwise cargo thinks they are unused) +#[allow(unused_extern_crates)] extern crate clippy_utils; +#[allow(unused_extern_crates)] extern crate derive_new; +#[allow(unused_extern_crates)] extern crate if_chain; +#[allow(unused_extern_crates)] extern crate itertools; +#[allow(unused_extern_crates)] extern crate quote; +#[allow(unused_extern_crates)] extern crate syn; fn host_lib() -> PathBuf { diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 4ede20c52583b..54f452172deb4 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -6,6 +6,8 @@ // Dogfood cannot run on Windows #![cfg(not(windows))] #![feature(once_cell)] +#![cfg_attr(feature = "deny-warnings", deny(warnings))] +#![warn(rust_2018_idioms, unused_lifetimes)] use std::lazy::SyncLazy; use std::path::PathBuf; diff --git a/tests/fmt.rs b/tests/fmt.rs index 7616d8001e885..be42f1fbb2023 100644 --- a/tests/fmt.rs +++ b/tests/fmt.rs @@ -1,3 +1,6 @@ +#![cfg_attr(feature = "deny-warnings", deny(warnings))] +#![warn(rust_2018_idioms, unused_lifetimes)] + use std::path::PathBuf; use std::process::Command; diff --git a/tests/integration.rs b/tests/integration.rs index 1718089e8bd27..7e3eff3c7324f 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -1,4 +1,6 @@ #![cfg(feature = "integration")] +#![cfg_attr(feature = "deny-warnings", deny(warnings))] +#![warn(rust_2018_idioms, unused_lifetimes)] use std::env; use std::ffi::OsStr; diff --git a/tests/lint_message_convention.rs b/tests/lint_message_convention.rs index 2f8989c8e1140..b4d94dc983fec 100644 --- a/tests/lint_message_convention.rs +++ b/tests/lint_message_convention.rs @@ -1,3 +1,6 @@ +#![cfg_attr(feature = "deny-warnings", deny(warnings))] +#![warn(rust_2018_idioms, unused_lifetimes)] + use std::ffi::OsStr; use std::path::PathBuf; diff --git a/tests/missing-test-files.rs b/tests/missing-test-files.rs index 9cef7438d225c..bd342e390f52f 100644 --- a/tests/missing-test-files.rs +++ b/tests/missing-test-files.rs @@ -1,3 +1,5 @@ +#![cfg_attr(feature = "deny-warnings", deny(warnings))] +#![warn(rust_2018_idioms, unused_lifetimes)] #![allow(clippy::assertions_on_constants)] use std::fs::{self, DirEntry}; diff --git a/tests/versioncheck.rs b/tests/versioncheck.rs index 1eaec4a50a6a6..77102b8cac0c9 100644 --- a/tests/versioncheck.rs +++ b/tests/versioncheck.rs @@ -1,4 +1,7 @@ +#![cfg_attr(feature = "deny-warnings", deny(warnings))] +#![warn(rust_2018_idioms, unused_lifetimes)] #![allow(clippy::single_match_else)] + use rustc_tools_util::VersionInfo; #[test] From fe247b4df7fcf22c99fad1f6f6af28bd1c088c73 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Wed, 8 Sep 2021 16:32:16 +0200 Subject: [PATCH 94/94] Update Cargo.lock --- Cargo.lock | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5000b1e051fa5..34f734f3bffc4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -293,7 +293,7 @@ dependencies = [ "humantime 2.0.1", "ignore", "im-rc", - "itertools 0.10.0", + "itertools 0.10.1", "jobserver", "lazy_static", "lazycell", @@ -388,7 +388,7 @@ dependencies = [ "flate2", "git2", "glob", - "itertools 0.10.0", + "itertools 0.10.1", "lazy_static", "remove_dir_all", "serde_json", @@ -554,13 +554,16 @@ dependencies = [ [[package]] name = "clippy" -version = "0.1.56" +version = "0.1.57" dependencies = [ "cargo_metadata 0.12.0", "clippy_lints", + "clippy_utils", "compiletest_rs 0.6.0", "derive-new", "filetime", + "if_chain", + "itertools 0.10.1", "quote", "regex", "rustc-workspace-hack", @@ -587,7 +590,7 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.56" +version = "0.1.57" dependencies = [ "cargo_metadata 0.12.0", "clippy_utils", @@ -608,14 +611,10 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.56" +version = "0.1.57" dependencies = [ "if_chain", - "itertools 0.9.0", - "regex-syntax", "rustc-semver", - "serde", - "unicode-normalization", ] [[package]] @@ -1744,9 +1743,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319" +checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" dependencies = [ "either", ]