From 8e563b5468d325262d38ecba873bfd3fd3708fa4 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Thu, 12 Aug 2021 10:48:16 +0200 Subject: [PATCH 01/29] 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 8b2c318acf84..9a89047f0727 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 02/29] 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 e182c99ce06a..2216bb9f293d 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 d856c55a41a4..0339de77f3ce 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 146b6fccd0c7..1f4d666c7a92 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 77efdec1e50d..56c00544c93a 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 acbefc8064dd..2b89170073be 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 7c39a3e2ce3d..0be460d67a75 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 a3a3603c4c0e..75561cfde369 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 0541ac5eccca..b64246515f34 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 ce23c0ce4a07..04fc5887e8e8 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 a666fee1a4ad..008ef661b55f 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 28db7233d70e..3ce91d421bac 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 429c6ed7d2d7..3736d237642f 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 a2cbfb1a05ee..a519ad90df54 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 f49b382c5ea3..dbdb4251b3be 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 699ddce0cff9..0e5121ca3d73 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 e97b7c941703..6d9f6215ed41 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 d57588716a5b..ef822e0cbe54 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 57e10ce42f8b..8829b8c5f4df 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 b4188d9ed309..99c03844f492 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 e8ad16bc0def..08d3a7ce92bb 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 1909fabb22fe..91606ed3b2bb 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 ef615b0aa40a..c1d22e5d72c1 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 000000000000..677aa80e1b76 --- /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 06fe967dafc4..b32feab4ee3e 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 2201cf56d52a..fff533167ede 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 3f0b23ee4d3e..ba8f9446af85 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 5088b8bb0d36..5a50cc48d61b 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 d9aa42fe8eeb..9a6ddc72ce56 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 e07518b25868..28e9e6f438e3 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 06c431babc23..f6254aa715a4 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 dbe9cbe0ded8..ca660a9250db 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 1222a95d4eaa..157b18c1f6b1 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 a79b2fe76e2d..7314bce83e03 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 e0930d69ab9f..77b6e60d8939 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 4fa8e77a67b7..f126908e84b0 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 ad7409fe3a9b..371bb8b445a7 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 9acfbc994b38..c8a231341b7e 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 9ed5e585f841..1164ac4938fb 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 44d3d4563428..a28b1d78f7d4 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 47336459d7da..a48a53850830 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 7c94474cb35d..71cfa196fc33 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 f32f1109b08e..884180f0586e 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 59f878f8b20a..1d59d6bfea1b 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 4d49b43bde9e..789079510c5e 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 e914dc1c222f..4f9aaf396b80 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 43d3792f5952..ff2e0417435b 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 bff657bc1103..23887f178454 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 a47677a1f3a2..33a3ef751363 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 a27ce945ca58..7551cac9f504 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 e9f042ddefcd..5b4adc868dff 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 86063a082808..87e962b9228c 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 00170e649e25..e863870e7d61 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 d76458c32891..dcb36b5951cb 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 57f344716a16..da14ddb25b37 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 fdcc9a33f55f..97c26bc83ad4 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 773b5914439d..18846c898da0 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 578bd8772679..01db27876da7 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 756e6e818ad4..38457c8ea4dd 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 c00b4c78cf28..f49b23924efe 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 834b9056e311..6b24675ac2d4 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 4390ff7dc304..c2f94d0e8575 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 75908c974cc9..afaf92961b02 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 9905029ce91f..b2bcbd38c2df 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 2b1bc1f48595..daa073414577 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 b24accd962a7..f621595abaea 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 0f8f839a0d54..ef518359ec5f 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 5763d9e82d48..8518659ccf31 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 f49bcfedf3a1..614d16ced40f 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 e7e0a31febc4..1a0123803a3e 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 2b0005bbff1d..9c4c49ceac47 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 000000000000..7ac3f426c977 --- /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 000000000000..82b727a039ed --- /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 000000000000..feb215b09f66 --- /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 52e80ceee83c..cdcdd808c944 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 5078a3c9028c..72f34257d1f4 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 cb0afeae15ee..ff9b08996da5 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 80bff87c6f22b98c5a7c0cb36233e4e2ba7e5a56 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Thu, 22 Jul 2021 21:56:07 +0800 Subject: [PATCH 03/29] 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 0be03969bcbe..3e35ada7b2a1 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 03eeb54d8d1c..5e559991c169 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 900d45317607..ee675838c4cb 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 0e6ead675c24..dee9d487c78e 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 4f9aaf396b80..a2221a0b283b 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 b97d4c062b4d99139f14960daa7ce99d63625833 Mon Sep 17 00:00:00 2001 From: Caio Date: Sun, 8 Aug 2021 11:49:13 -0300 Subject: [PATCH 04/29] 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 cb9347a923d8..891e865b245d 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 9b2e4f8998e4..51d95cc6f0b1 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 a403a9846bab..6b63c2cf157a 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 2dcd55457993..5eb99cfe24f4 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 ded7001ad8c8..7825e5f6ed52 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 e1d0d65edb1b..627f746ec997 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 192b69e18f90..f6a64a8ca603 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 b01c0cdd8462..d12482e7b7bb 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 d3ddeda9fd1b..e2d3905eacb5 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 587307811a11..cd813c639dbb 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 17b9a2f888e0..a2dac57454f2 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 0a7d31dce2fd..79d4d7ddcbce 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 8c1f10733095..f52f090d3872 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 2411a3175b91..58646385def5 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 13f0d43cf8dd..0594b73dd383 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 64ff7574f86b..9f2bc3c7ebae 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 a98e2dc1372d..2525b14e1c5c 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 7ca54d539720..bd9de5e08d73 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 1e54a1e2de16..344dc5074d36 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 3810d0dcc051..3f77e7af927a 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 6d9f6215ed41..2c46971d5f74 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 9c1720798529..6be410ca8e3c 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 ef822e0cbe54..0757d329125c 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 7dec1595e0d1..53d97f775435 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 db12c377488b..4e040508b6bf 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 5360c02f9053..a183d0c66e8c 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 a49851de38e1..6954da67e32c 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 d5032c5ba7f2..610152a217f1 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 36f2829a5b94..c9dd94400efb 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 f6254aa715a4..ac21eb5275f0 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 7aef3a5f34cf..d0b0bad5eb1c 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 4534f6e25165..35cff4141a90 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 0e682c692c7a..91085c13ac4a 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 0179bd48ee3c..0114a2f97a22 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 eeeac35a6d51..530b3396abef 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 db4b1002ce12..e153288aa58d 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 a8e962d1af3f..44d5ff0b63ad 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 c8a231341b7e..d6cf7190abb0 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 c5b8acb9982d..bffd9f3612b0 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 61fd375a9892..f93d7782e251 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 f7ddee12dcf6..6bf216cec167 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 32fa46f042ce..95a45fa937f1 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 30c2260d15ca..7ea07a15aea5 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 88b115a63d78..29e2559fc6d6 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 884180f0586e..29b698e56e3c 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 fd70553e064b..a44f2df2fd63 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 1d59d6bfea1b..82bfce8fe789 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 3bd75b10e905..3b494e1fc853 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 502b38545b8e..1653de9a6f26 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 779788849008..f96917f58334 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 aaa422d88c3e..85fa42103219 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 bb238c81ebc0..553c840f9b08 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 3d8fb8507e51..e518b2677b7b 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 1fcb29eff18e..2736753c14b6 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 44c51e8112a7..c84e31ea482a 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 f25a66d634e8..359fa49f51be 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 295225b660e1ddfd4a476a14aa12b37ae140f91d Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Wed, 2 Dec 2020 15:16:12 -0800 Subject: [PATCH 05/29] 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 cece28e8b3c3..000000000000 --- 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 f49b382c5ea3..e455c8db03f5 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 cdbde79b19eb..000000000000 --- 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 397b893aed96..000000000000 --- 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 1ddc12f9ab21..000000000000 --- 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 4b9bfef79748..000000000000 --- 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 590699025072..000000000000 --- 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 de7026f3ffaf..000000000000 --- 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 5ee5182ca051..000000000000 --- 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 3ceff27d9ad5..000000000000 --- 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 550bdb001e4c..000000000000 --- 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 694548ece97b..000000000000 --- 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 40a00ba3de35..000000000000 --- 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 bbaf234d3c9f..000000000000 --- 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 ecbb05c7fbc3..000000000000 --- 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 558ae55518d5..000000000000 --- 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 a0d5d7c40103..000000000000 --- 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 5703135bcf1e..000000000000 --- 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 4ba9f0c1fcff..1943d0092e62 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 c0002e535431..51048e45c067 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 d5e51dbf10c116f899abc275a6e36b8977b0ebfa Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 16 Jul 2021 08:54:08 -0500 Subject: [PATCH 06/29] 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 29b698e56e3c..6437363bad74 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 4123fedac5e0315a7f462d870d2ce2c5563d07e1 Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Fri, 6 Aug 2021 17:14:27 +0200 Subject: [PATCH 07/29] 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 4a83d35a568c..6f12d34e66b6 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 75561cfde369..cb2b7f5be70a 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 6f73a00d1f78..19719502870b 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 08/29] 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 6b63c2cf157a..a42eee53459e 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 e2d3905eacb5..7dad1c31150e 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 cd813c639dbb..fb5637fcec18 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 9f2bc3c7ebae..5852674da578 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 6be410ca8e3c..d6d3315e0a83 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 53d97f775435..161d88414907 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 a183d0c66e8c..3d0da472ddcb 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 d0b0bad5eb1c..eff3d3abff80 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 91085c13ac4a..7b6a0894e6d2 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 29b698e56e3c..957ec35be6f9 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 82bfce8fe789..7d7c3b8846c3 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 55467cf4229d..4ce365cc7649 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 f96917f58334..c119570e8abd 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 8975b2efbaee..55e70dce208a 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 62a25ce2d128..1bddc47721e5 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 234ff5e9e80e..d4a52ec9881d 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 134ce9d24116..bc3a5e7698d7 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 09/29] 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 dee9d487c78e..8e544f580665 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 10/29] 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 ee675838c4cb..db0f412f2a18 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 4553ac704a28..85d1f65c51f0 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 82bfce8fe789..0da86a76d39e 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 11/29] =?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 2525b14e1c5c..2296842e86f5 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 aa3067876ebf..6c2563358caf 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 0114a2f97a22..4fa361fedafa 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 b28a37cabd40..b9e317a3cfd0 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 a28b1d78f7d4..c192f9094a8a 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 15c27d1a996d..9ba1381da659 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 82bfce8fe789..2a13b5ee3474 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 3b494e1fc853..65d93e8f86e4 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 535147ccc645..256a336db821 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 12/29] =?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 b4cf1971d78d..4dd0ffe77ea4 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 6c2563358caf..3f9110295fc6 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 13/29] =?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 d6d7049fb61b..3258c9fb3fed 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 4d627fc41b0a34d99afbaa21f3b8c301d942ffc8 Mon Sep 17 00:00:00 2001 From: lcnr Date: Sat, 13 Mar 2021 16:05:15 +0100 Subject: [PATCH 14/29] 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 530b3396abef..2335d2248aa9 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 15/29] 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 3f9110295fc6..2a85a67fa099 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 2335d2248aa9..f5e43264a5c6 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 9ba1381da659..8bf31807d55d 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 16/29] 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 8b0e9e6bc9b9..685dbf26250c 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 8992d25932ca..89146b4dd2c9 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 0f6cd5de761f..545498a10478 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 91606ed3b2bb..9626cf79dc12 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 e153288aa58d..681baed8c369 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 4472edecbed8..4ba5e1a0f535 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 6fc5707a4eef..97b1b2dae3c1 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 a3601cca2eff..9ae50e47ca4c 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 32a73984674a..98f3937ba3dd 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 a2221a0b283b..3cd8ed5aa2c8 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 17/29] 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 5e559991c169..90b2aa168962 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 98f3937ba3dd..ddff1686ba2c 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 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 18/29] 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 d358e9fb876a..da86d28ee0b2 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 19/29] 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 da86d28ee0b2..940eee7a7889 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 977e6d966e87..667cdd830252 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 6bf216cec167..e97983a2e145 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 20/29] 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 8afef6b23d47..8b20997fdf8d 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 21/29] `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 31e53e846d54..405e3039e7d0 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 9bb2e227fc12..e3b3b27a6fc3 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 8b20997fdf8d..8b0c0f304fce 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 6d2cbb6ad96f..aa60d0504e5e 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 0accb516f5f6..baa7eec05462 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 63c211f39fa1..b89cc6451bb5 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 22/29] 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 ac21eb5275f0..2ffc00b449d0 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 7ea07a15aea5..133f6c29f7d2 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 5722a7dacdfa84d125adaa872dfe2ec0a34e8200 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 26 Aug 2021 10:05:33 -0500 Subject: [PATCH 23/29] 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 f5683856889f..86226be715d3 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 385a3f546b9d..b5f573cb104e 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 d20739a9f74c..61e672781f84 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 7e03cf56e8a8..6cecdd6b1996 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 24/29] 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 891e865b245d..d834a1d317a0 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 d12482e7b7bb..a643b3eb8f1b 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 ea6193acbe84..77d08081c07f 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 fb5637fcec18..33eba02dae06 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 bd9de5e08d73..9c224445b159 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 d6d3315e0a83..1848f5b5de2f 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 131ed1d8925f..2f1ff567e844 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 35cff4141a90..e7bc24465908 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 17c9e3e7541c..e79cd7ed4ec4 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 4fa361fedafa..87364a88ed0d 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 7314bce83e03..90e3c3f4b3e9 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 681baed8c369..341b5a61631d 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 3a6a07c52263..f4808682b692 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 e97983a2e145..43590cc78623 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 95a45fa937f1..85ff292c53a4 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 61e672781f84..4bb31868bafb 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 a44f2df2fd63..6e9a1de21eef 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 6cecdd6b1996..40d7963a5e54 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 25/29] 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 570883703437..d86414a87f4d 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 e5bbf75c3b0a..6cb2bd7f6efb 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 26/29] 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 9c224445b159..97cbe2c53dda 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 0757d329125c..79527e3bfa92 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 4bb31868bafb..05a4a0143195 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 27/29] 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 91533695eb30..188d0419c399 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 28/29] 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 23887f178454..8b0563eda6d1 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 29/29] 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 86226be715d3..a4693fa213bc 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 5ce5f809f469..2b76eacb7d61 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 80b4f544b1a4..15f6dcae8870 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 d660008e7d18..42d51272279e 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 65d93e8f86e4..ab05a0b42385 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 {