From cb29e3effbf72db2e1f5177336bfb3309ec8805e Mon Sep 17 00:00:00 2001 From: cameron Date: Tue, 1 Feb 2022 11:21:42 +0000 Subject: [PATCH 01/79] add doc_link_with_quotes lint --- CHANGELOG.md | 1 + clippy_lints/src/doc_link_with_quotes.rs | 60 +++++++++++++++++++++++ clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_pedantic.rs | 1 + clippy_lints/src/lib.rs | 2 + tests/ui/doc_link_with_quotes.rs | 12 +++++ tests/ui/doc_link_with_quotes.stderr | 10 ++++ 7 files changed, 87 insertions(+) create mode 100644 clippy_lints/src/doc_link_with_quotes.rs create mode 100644 tests/ui/doc_link_with_quotes.rs create mode 100644 tests/ui/doc_link_with_quotes.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 9548366daf9a1..42b002407b656 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2941,6 +2941,7 @@ Released 2018-09-13 [`disallowed_script_idents`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents [`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression +[`doc_link_with_quotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_with_quotes [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons [`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use diff --git a/clippy_lints/src/doc_link_with_quotes.rs b/clippy_lints/src/doc_link_with_quotes.rs new file mode 100644 index 0000000000000..cb07f57e87006 --- /dev/null +++ b/clippy_lints/src/doc_link_with_quotes.rs @@ -0,0 +1,60 @@ +use clippy_utils::diagnostics::span_lint; +use itertools::Itertools; +use rustc_ast::{AttrKind, Attribute}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Detects the syntax `['foo']` in documentation comments (notice quotes instead of backticks) + /// outside of code blocks + /// ### Why is this bad? + /// It is likely a typo when defining an intra-doc link + /// + /// ### Example + /// ```rust + /// /// See also: ['foo'] + /// fn bar() {} + /// ``` + /// Use instead: + /// ```rust + /// /// See also: [`foo`] + /// fn bar() {} + /// ``` + #[clippy::version = "1.60.0"] + pub DOC_LINK_WITH_QUOTES, + pedantic, + "possible typo for an intra-doc link" +} +declare_lint_pass!(DocLinkWithQuotes => [DOC_LINK_WITH_QUOTES]); + +impl EarlyLintPass for DocLinkWithQuotes { + fn check_attribute(&mut self, ctx: &EarlyContext<'_>, attr: &Attribute) { + if let AttrKind::DocComment(_, symbol) = attr.kind { + if contains_quote_link(symbol.as_str()) { + span_lint( + ctx, + DOC_LINK_WITH_QUOTES, + attr.span, + "possible intra-doc link using quotes instead of backticks", + ); + } + } + } +} + +fn contains_quote_link(s: &str) -> bool { + let mut in_backticks = false; + let mut found_opening = false; + + for c in s.chars().tuple_windows::<(char, char)>() { + match c { + ('`', _) => in_backticks = !in_backticks, + ('[', '\'') if !in_backticks => found_opening = true, + ('\'', ']') if !in_backticks && found_opening => return true, + _ => {}, + } + } + + false +} diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index e5e1c052c15ed..42c530428f369 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -109,6 +109,7 @@ store.register_lints(&[ doc::MISSING_PANICS_DOC, doc::MISSING_SAFETY_DOC, doc::NEEDLESS_DOCTEST_MAIN, + doc_link_with_quotes::DOC_LINK_WITH_QUOTES, double_comparison::DOUBLE_COMPARISONS, double_parens::DOUBLE_PARENS, drop_forget_ref::DROP_COPY, diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs index 1292675f4a96c..de72420706bac 100644 --- a/clippy_lints/src/lib.register_pedantic.rs +++ b/clippy_lints/src/lib.register_pedantic.rs @@ -28,6 +28,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(doc::DOC_MARKDOWN), LintId::of(doc::MISSING_ERRORS_DOC), LintId::of(doc::MISSING_PANICS_DOC), + LintId::of(doc_link_with_quotes::DOC_LINK_WITH_QUOTES), LintId::of(empty_enum::EMPTY_ENUM), LintId::of(enum_variants::MODULE_NAME_REPETITIONS), LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 9d42185dc0ec9..644c24182890d 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -197,6 +197,7 @@ mod disallowed_methods; mod disallowed_script_idents; mod disallowed_types; mod doc; +mod doc_link_with_quotes; mod double_comparison; mod double_parens; mod drop_forget_ref; @@ -861,6 +862,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(borrow_as_ptr::BorrowAsPtr::new(msrv))); store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv))); store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation)); + store.register_early_pass(|| Box::new(doc_link_with_quotes::DocLinkWithQuotes)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/tests/ui/doc_link_with_quotes.rs b/tests/ui/doc_link_with_quotes.rs new file mode 100644 index 0000000000000..ab52fb1a4a69e --- /dev/null +++ b/tests/ui/doc_link_with_quotes.rs @@ -0,0 +1,12 @@ +#![warn(clippy::doc_link_with_quotes)] + +fn main() { + foo() +} + +/// Calls ['bar'] +pub fn foo() { + bar() +} + +pub fn bar() {} diff --git a/tests/ui/doc_link_with_quotes.stderr b/tests/ui/doc_link_with_quotes.stderr new file mode 100644 index 0000000000000..bf6d57d8afe8c --- /dev/null +++ b/tests/ui/doc_link_with_quotes.stderr @@ -0,0 +1,10 @@ +error: possible intra-doc link using quotes instead of backticks + --> $DIR/doc_link_with_quotes.rs:7:1 + | +LL | /// Calls ['bar'] + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::doc-link-with-quotes` implied by `-D warnings` + +error: aborting due to previous error + From ee8fae3f5d4aac2bcbea8936e39d3027aade8e32 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Wed, 4 May 2022 18:13:06 +0100 Subject: [PATCH 02/79] identity_op: add parenthesis to suggestions where required --- clippy_lints/src/identity_op.rs | 125 ++++++++------- tests/ui/identity_op.fixed | 119 +++++++++++++++ tests/ui/identity_op.rs | 63 ++++---- tests/ui/identity_op.stderr | 260 ++++++++++++++++++-------------- tests/ui/modulo_one.rs | 2 +- tests/ui/modulo_one.stderr | 10 +- 6 files changed, 377 insertions(+), 202 deletions(-) create mode 100644 tests/ui/identity_op.fixed diff --git a/clippy_lints/src/identity_op.rs b/clippy_lints/src/identity_op.rs index 40cc5cd4bcf9d..419ea5a6811b8 100644 --- a/clippy_lints/src/identity_op.rs +++ b/clippy_lints/src/identity_op.rs @@ -1,15 +1,14 @@ -use clippy_utils::get_parent_expr; -use clippy_utils::source::snippet; -use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind}; +use clippy_utils::consts::{constant_full_int, constant_simple, Constant, FullInt}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{clip, unsext}; +use rustc_errors::Applicability; +use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use clippy_utils::consts::{constant_full_int, constant_simple, Constant, FullInt}; -use clippy_utils::diagnostics::span_lint; -use clippy_utils::{clip, unsext}; - declare_clippy_lint! { /// ### What it does /// Checks for identity operations, e.g., `x + 0`. @@ -23,11 +22,6 @@ declare_clippy_lint! { /// # let x = 1; /// x / 1 + 0 * 1 - 0 | 0; /// ``` - /// - /// ### Known problems - /// False negatives: `f(0 + if b { 1 } else { 2 } + 3);` is reducible to - /// `f(if b { 1 } else { 2 } + 3);`. But the lint doesn't trigger for the code. - /// See [#8724](https://github.com/rust-lang/rust-clippy/issues/8724) #[clippy::version = "pre 1.29.0"] pub IDENTITY_OP, complexity, @@ -45,31 +39,22 @@ impl<'tcx> LateLintPass<'tcx> for IdentityOp { if !is_allowed(cx, *cmp, left, right) { match cmp.node { BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => { - if reducible_to_right(cx, expr, right) { - check(cx, left, 0, expr.span, right.span); - } - check(cx, right, 0, expr.span, left.span); + check(cx, left, 0, expr.span, right.span, needs_parenthesis(cx, expr, right)); + check(cx, right, 0, expr.span, left.span, Parens::Unneeded); }, BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => { - check(cx, right, 0, expr.span, left.span); + check(cx, right, 0, expr.span, left.span, Parens::Unneeded); }, BinOpKind::Mul => { - if reducible_to_right(cx, expr, right) { - check(cx, left, 1, expr.span, right.span); - } - check(cx, right, 1, expr.span, left.span); + check(cx, left, 1, expr.span, right.span, needs_parenthesis(cx, expr, right)); + check(cx, right, 1, expr.span, left.span, Parens::Unneeded); }, - BinOpKind::Div => check(cx, right, 1, expr.span, left.span), + BinOpKind::Div => check(cx, right, 1, expr.span, left.span, Parens::Unneeded), BinOpKind::BitAnd => { - if reducible_to_right(cx, expr, right) { - check(cx, left, -1, expr.span, right.span); - } - check(cx, right, -1, expr.span, left.span); - }, - BinOpKind::Rem => { - // Don't call reducible_to_right because N % N is always reducible to 1 - check_remainder(cx, left, right, expr.span, left.span); + check(cx, left, -1, expr.span, right.span, needs_parenthesis(cx, expr, right)); + check(cx, right, -1, expr.span, left.span, Parens::Unneeded); }, + BinOpKind::Rem => check_remainder(cx, left, right, expr.span, left.span), _ => (), } } @@ -77,24 +62,50 @@ impl<'tcx> LateLintPass<'tcx> for IdentityOp { } } -/// Checks if `left op ..right` can be actually reduced to `right` -/// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` -/// cannot be reduced to `if b { 1 } else { 2 } + if b { 3 } else { 4 }` +#[derive(Copy, Clone)] +enum Parens { + Needed, + Unneeded, +} + +/// Checks if `left op right` needs parenthesis when reduced to `right` +/// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` cannot be reduced +/// to `if b { 1 } else { 2 } + if b { 3 } else { 4 }` where the `if` could be +/// interpreted as a statement +/// /// See #8724 -fn reducible_to_right(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>) -> bool { - if let ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) | ExprKind::Loop(..) = right.kind { - is_toplevel_binary(cx, binary) - } else { - true +fn needs_parenthesis(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>) -> Parens { + match right.kind { + ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => { + // ensure we're checking against the leftmost expression of `right` + // + // ~~~ `lhs` + // 0 + {4} * 2 + // ~~~~~~~ `right` + return needs_parenthesis(cx, binary, lhs); + }, + ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) | ExprKind::Loop(..) => {}, + _ => return Parens::Unneeded, } -} -fn is_toplevel_binary(cx: &LateContext<'_>, must_be_binary: &Expr<'_>) -> bool { - if let Some(parent) = get_parent_expr(cx, must_be_binary) && let ExprKind::Binary(..) = &parent.kind { - false - } else { - true + let mut prev_id = binary.hir_id; + for (_, node) in cx.tcx.hir().parent_iter(binary.hir_id) { + if let Node::Expr(expr) = node + && let ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) = expr.kind + && lhs.hir_id == prev_id + { + // keep going until we find a node that encompasses left of `binary` + prev_id = expr.hir_id; + continue; + } + + match node { + Node::Block(_) | Node::Stmt(_) => break, + _ => return Parens::Unneeded, + }; } + + Parens::Needed } fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> bool { @@ -115,11 +126,11 @@ fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span (Some(FullInt::U(lv)), Some(FullInt::U(rv))) => lv < rv, _ => return, } { - span_ineffective_operation(cx, span, arg); + span_ineffective_operation(cx, span, arg, Parens::Unneeded); } } -fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) { +fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens) { if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e).map(Constant::peel_refs) { let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() { ty::Int(ity) => unsext(cx.tcx, -1_i128, ity), @@ -132,19 +143,27 @@ fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) { 1 => v == 1, _ => unreachable!(), } { - span_ineffective_operation(cx, span, arg); + span_ineffective_operation(cx, span, arg, parens); } } } -fn span_ineffective_operation(cx: &LateContext<'_>, span: Span, arg: Span) { - span_lint( +fn span_ineffective_operation(cx: &LateContext<'_>, span: Span, arg: Span, parens: Parens) { + let mut applicability = Applicability::MachineApplicable; + let expr_snippet = snippet_with_applicability(cx, arg, "..", &mut applicability); + + let suggestion = match parens { + Parens::Needed => format!("({expr_snippet})"), + Parens::Unneeded => expr_snippet.into_owned(), + }; + + span_lint_and_sugg( cx, IDENTITY_OP, span, - &format!( - "the operation is ineffective. Consider reducing it to `{}`", - snippet(cx, arg, "..") - ), + "this operation has no effect", + "consider reducing it to", + suggestion, + applicability, ); } diff --git a/tests/ui/identity_op.fixed b/tests/ui/identity_op.fixed new file mode 100644 index 0000000000000..5f9cebe212abd --- /dev/null +++ b/tests/ui/identity_op.fixed @@ -0,0 +1,119 @@ +// run-rustfix + +#![warn(clippy::identity_op)] +#![allow( + clippy::eq_op, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::op_ref, + clippy::double_parens, + unused +)] + +use std::fmt::Write as _; + +const ONE: i64 = 1; +const NEG_ONE: i64 = -1; +const ZERO: i64 = 0; + +struct A(String); + +impl std::ops::Shl for A { + type Output = A; + fn shl(mut self, other: i32) -> Self { + let _ = write!(self.0, "{}", other); + self + } +} + +struct Length(u8); +struct Meter; + +impl core::ops::Mul for u8 { + type Output = Length; + fn mul(self, _: Meter) -> Length { + Length(self) + } +} + +#[rustfmt::skip] +fn main() { + let x = 0; + + x; + x; + x + 1; + x; + 1 + x; + x - ZERO; //no error, as we skip lookups (for now) + x; + ((ZERO)) | x; //no error, as we skip lookups (for now) + + x; + x; + x / ONE; //no error, as we skip lookups (for now) + + x / 2; //no false positive + + x & NEG_ONE; //no error, as we skip lookups (for now) + x; + + let u: u8 = 0; + u; + + 1 << 0; // no error, this case is allowed, see issue 3430 + 42; + 1; + 42; + &x; + x; + + let mut a = A("".into()); + let b = a << 0; // no error: non-integer + + 1 * Meter; // no error: non-integer + + 2; + -2; + 2 + x; + -2 + x; + x + 1; + (x + 1) % 3; // no error + 4 % 3; // no error + 4 % -3; // no error + + // See #8724 + let a = 0; + let b = true; + (if b { 1 } else { 2 }); + (if b { 1 } else { 2 }) + if b { 3 } else { 4 }; + (match a { 0 => 10, _ => 20 }); + (match a { 0 => 10, _ => 20 }) + match a { 0 => 30, _ => 40 }; + (if b { 1 } else { 2 }) + match a { 0 => 30, _ => 40 }; + (match a { 0 => 10, _ => 20 }) + if b { 3 } else { 4 }; + (if b { 1 } else { 2 }); + + ({ a }) + 3; + ({ a } * 2); + (loop { let mut c = 0; if c == 10 { break c; } c += 1; }) + { a * 2 }; + + fn f(_: i32) { + todo!(); + } + f(a + { 8 * 5 }); + f(if b { 1 } else { 2 } + 3); + const _: i32 = { 2 * 4 } + 3; + const _: i32 = { 1 + 2 * 3 } + 3; + + a as usize; + let _ = a as usize; + ({ a } as usize); + + 2 * { a }; + (({ a } + 4)); + 1; +} + +pub fn decide(a: bool, b: bool) -> u32 { + (if a { 1 } else { 2 }) + if b { 3 } else { 5 } +} diff --git a/tests/ui/identity_op.rs b/tests/ui/identity_op.rs index fec54d00ccb4b..ca799c9cfac0f 100644 --- a/tests/ui/identity_op.rs +++ b/tests/ui/identity_op.rs @@ -1,3 +1,15 @@ +// run-rustfix + +#![warn(clippy::identity_op)] +#![allow( + clippy::eq_op, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::op_ref, + clippy::double_parens, + unused +)] + use std::fmt::Write as _; const ONE: i64 = 1; @@ -24,14 +36,6 @@ impl core::ops::Mul for u8 { } } -#[allow( - clippy::eq_op, - clippy::no_effect, - clippy::unnecessary_operation, - clippy::op_ref, - clippy::double_parens -)] -#[warn(clippy::identity_op)] #[rustfmt::skip] fn main() { let x = 0; @@ -82,29 +86,34 @@ fn main() { let a = 0; let b = true; 0 + if b { 1 } else { 2 }; - 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }; // no error + 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }; 0 + match a { 0 => 10, _ => 20 }; - 0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 }; // no error - 0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 }; // no error - 0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 }; // no error - - 0 + if b { 0 + 1 } else { 2 }; - 0 + match a { 0 => 0 + 10, _ => 20 }; - 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 }; - - let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 }; - let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 }; - - 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 } + 0; - - 0 + { a } + 3; // no error - 0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 }; // no error - + 0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 }; + 0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 }; + 0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 }; + (if b { 1 } else { 2 }) + 0; + + 0 + { a } + 3; + 0 + { a } * 2; + 0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 }; + fn f(_: i32) { todo!(); } f(1 * a + { 8 * 5 }); - f(0 + if b { 1 } else { 2 } + 3); // no error + f(0 + if b { 1 } else { 2 } + 3); const _: i32 = { 2 * 4 } + 0 + 3; - const _: i32 = 0 + { 1 + 2 * 3 } + 3; // no error + const _: i32 = 0 + { 1 + 2 * 3 } + 3; + + 0 + a as usize; + let _ = 0 + a as usize; + 0 + { a } as usize; + + 2 * (0 + { a }); + 1 * ({ a } + 4); + 1 * 1; +} + +pub fn decide(a: bool, b: bool) -> u32 { + 0 + if a { 1 } else { 2 } + if b { 3 } else { 5 } } diff --git a/tests/ui/identity_op.stderr b/tests/ui/identity_op.stderr index d8cb65839cbba..1a104a20b841c 100644 --- a/tests/ui/identity_op.stderr +++ b/tests/ui/identity_op.stderr @@ -1,202 +1,238 @@ -error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:39:5 +error: this operation has no effect + --> $DIR/identity_op.rs:43:5 | LL | x + 0; - | ^^^^^ + | ^^^^^ help: consider reducing it to: `x` | = note: `-D clippy::identity-op` implied by `-D warnings` -error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:40:5 +error: this operation has no effect + --> $DIR/identity_op.rs:44:5 | LL | x + (1 - 1); - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: consider reducing it to: `x` -error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:42:5 +error: this operation has no effect + --> $DIR/identity_op.rs:46:5 | LL | 0 + x; - | ^^^^^ + | ^^^^^ help: consider reducing it to: `x` -error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:45:5 +error: this operation has no effect + --> $DIR/identity_op.rs:49:5 | LL | x | (0); - | ^^^^^^^ + | ^^^^^^^ help: consider reducing it to: `x` -error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:48:5 +error: this operation has no effect + --> $DIR/identity_op.rs:52:5 | LL | x * 1; - | ^^^^^ + | ^^^^^ help: consider reducing it to: `x` -error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:49:5 +error: this operation has no effect + --> $DIR/identity_op.rs:53:5 | LL | 1 * x; - | ^^^^^ + | ^^^^^ help: consider reducing it to: `x` -error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:55:5 +error: this operation has no effect + --> $DIR/identity_op.rs:59:5 | LL | -1 & x; - | ^^^^^^ + | ^^^^^^ help: consider reducing it to: `x` -error: the operation is ineffective. Consider reducing it to `u` - --> $DIR/identity_op.rs:58:5 +error: this operation has no effect + --> $DIR/identity_op.rs:62:5 | LL | u & 255; - | ^^^^^^^ + | ^^^^^^^ help: consider reducing it to: `u` -error: the operation is ineffective. Consider reducing it to `42` - --> $DIR/identity_op.rs:61:5 +error: this operation has no effect + --> $DIR/identity_op.rs:65:5 | LL | 42 << 0; - | ^^^^^^^ + | ^^^^^^^ help: consider reducing it to: `42` -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:62:5 +error: this operation has no effect + --> $DIR/identity_op.rs:66:5 | LL | 1 >> 0; - | ^^^^^^ + | ^^^^^^ help: consider reducing it to: `1` -error: the operation is ineffective. Consider reducing it to `42` - --> $DIR/identity_op.rs:63:5 +error: this operation has no effect + --> $DIR/identity_op.rs:67:5 | LL | 42 >> 0; - | ^^^^^^^ + | ^^^^^^^ help: consider reducing it to: `42` -error: the operation is ineffective. Consider reducing it to `&x` - --> $DIR/identity_op.rs:64:5 +error: this operation has no effect + --> $DIR/identity_op.rs:68:5 | LL | &x >> 0; - | ^^^^^^^ + | ^^^^^^^ help: consider reducing it to: `&x` -error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:65:5 +error: this operation has no effect + --> $DIR/identity_op.rs:69:5 | LL | x >> &0; - | ^^^^^^^ + | ^^^^^^^ help: consider reducing it to: `x` -error: the operation is ineffective. Consider reducing it to `2` - --> $DIR/identity_op.rs:72:5 +error: this operation has no effect + --> $DIR/identity_op.rs:76:5 | LL | 2 % 3; - | ^^^^^ + | ^^^^^ help: consider reducing it to: `2` -error: the operation is ineffective. Consider reducing it to `-2` - --> $DIR/identity_op.rs:73:5 +error: this operation has no effect + --> $DIR/identity_op.rs:77:5 | LL | -2 % 3; - | ^^^^^^ + | ^^^^^^ help: consider reducing it to: `-2` -error: the operation is ineffective. Consider reducing it to `2` - --> $DIR/identity_op.rs:74:5 +error: this operation has no effect + --> $DIR/identity_op.rs:78:5 | LL | 2 % -3 + x; - | ^^^^^^ + | ^^^^^^ help: consider reducing it to: `2` -error: the operation is ineffective. Consider reducing it to `-2` - --> $DIR/identity_op.rs:75:5 +error: this operation has no effect + --> $DIR/identity_op.rs:79:5 | LL | -2 % -3 + x; - | ^^^^^^^ + | ^^^^^^^ help: consider reducing it to: `-2` -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:76:9 +error: this operation has no effect + --> $DIR/identity_op.rs:80:9 | LL | x + 1 % 3; - | ^^^^^ + | ^^^^^ help: consider reducing it to: `1` -error: the operation is ineffective. Consider reducing it to `if b { 1 } else { 2 }` - --> $DIR/identity_op.rs:84:5 +error: this operation has no effect + --> $DIR/identity_op.rs:88:5 | LL | 0 + if b { 1 } else { 2 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })` -error: the operation is ineffective. Consider reducing it to `match a { 0 => 10, _ => 20 }` - --> $DIR/identity_op.rs:86:5 +error: this operation has no effect + --> $DIR/identity_op.rs:89:5 + | +LL | 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })` + +error: this operation has no effect + --> $DIR/identity_op.rs:90:5 | LL | 0 + match a { 0 => 10, _ => 20 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(match a { 0 => 10, _ => 20 })` -error: the operation is ineffective. Consider reducing it to `if b { 0 + 1 } else { 2 }` +error: this operation has no effect --> $DIR/identity_op.rs:91:5 | -LL | 0 + if b { 0 + 1 } else { 2 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | 0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(match a { 0 => 10, _ => 20 })` -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:91:16 +error: this operation has no effect + --> $DIR/identity_op.rs:92:5 | -LL | 0 + if b { 0 + 1 } else { 2 }; - | ^^^^^ +LL | 0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })` -error: the operation is ineffective. Consider reducing it to `match a { 0 => 0 + 10, _ => 20 }` - --> $DIR/identity_op.rs:92:5 +error: this operation has no effect + --> $DIR/identity_op.rs:93:5 + | +LL | 0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(match a { 0 => 10, _ => 20 })` + +error: this operation has no effect + --> $DIR/identity_op.rs:94:5 + | +LL | (if b { 1 } else { 2 }) + 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })` + +error: this operation has no effect + --> $DIR/identity_op.rs:96:5 | -LL | 0 + match a { 0 => 0 + 10, _ => 20 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | 0 + { a } + 3; + | ^^^^^^^^^ help: consider reducing it to: `({ a })` -error: the operation is ineffective. Consider reducing it to `10` - --> $DIR/identity_op.rs:92:25 +error: this operation has no effect + --> $DIR/identity_op.rs:97:5 | -LL | 0 + match a { 0 => 0 + 10, _ => 20 }; - | ^^^^^^ +LL | 0 + { a } * 2; + | ^^^^^^^^^^^^^ help: consider reducing it to: `({ a } * 2)` -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:93:16 +error: this operation has no effect + --> $DIR/identity_op.rs:98:5 | -LL | 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 }; - | ^^^^^ +LL | 0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(loop { let mut c = 0; if c == 10 { break c; } c += 1; })` -error: the operation is ineffective. Consider reducing it to `30` - --> $DIR/identity_op.rs:93:52 +error: this operation has no effect + --> $DIR/identity_op.rs:103:7 | -LL | 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 }; - | ^^^^^^ +LL | f(1 * a + { 8 * 5 }); + | ^^^^^ help: consider reducing it to: `a` -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:95:20 +error: this operation has no effect + --> $DIR/identity_op.rs:104:7 | -LL | let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 }; - | ^^^^^ +LL | f(0 + if b { 1 } else { 2 } + 3); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `if b { 1 } else { 2 }` -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:95:52 +error: this operation has no effect + --> $DIR/identity_op.rs:105:20 | -LL | let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 }; - | ^^^^^ +LL | const _: i32 = { 2 * 4 } + 0 + 3; + | ^^^^^^^^^^^^^ help: consider reducing it to: `{ 2 * 4 }` -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:96:23 +error: this operation has no effect + --> $DIR/identity_op.rs:106:20 | -LL | let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 }; - | ^^^^^ +LL | const _: i32 = 0 + { 1 + 2 * 3 } + 3; + | ^^^^^^^^^^^^^^^^^ help: consider reducing it to: `{ 1 + 2 * 3 }` -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:96:58 +error: this operation has no effect + --> $DIR/identity_op.rs:108:5 | -LL | let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 }; - | ^^^^^ +LL | 0 + a as usize; + | ^^^^^^^^^^^^^^ help: consider reducing it to: `a as usize` -error: the operation is ineffective. Consider reducing it to `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` - --> $DIR/identity_op.rs:98:5 +error: this operation has no effect + --> $DIR/identity_op.rs:109:13 | -LL | 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 } + 0; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _ = 0 + a as usize; + | ^^^^^^^^^^^^^^ help: consider reducing it to: `a as usize` -error: the operation is ineffective. Consider reducing it to `a` - --> $DIR/identity_op.rs:106:7 +error: this operation has no effect + --> $DIR/identity_op.rs:110:5 | -LL | f(1 * a + { 8 * 5 }); - | ^^^^^ +LL | 0 + { a } as usize; + | ^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `({ a } as usize)` -error: the operation is ineffective. Consider reducing it to `{ 2 * 4 }` - --> $DIR/identity_op.rs:108:20 +error: this operation has no effect + --> $DIR/identity_op.rs:112:9 | -LL | const _: i32 = { 2 * 4 } + 0 + 3; - | ^^^^^^^^^^^^^ +LL | 2 * (0 + { a }); + | ^^^^^^^^^^^ help: consider reducing it to: `{ a }` + +error: this operation has no effect + --> $DIR/identity_op.rs:113:5 + | +LL | 1 * ({ a } + 4); + | ^^^^^^^^^^^^^^^ help: consider reducing it to: `(({ a } + 4))` + +error: this operation has no effect + --> $DIR/identity_op.rs:114:5 + | +LL | 1 * 1; + | ^^^^^ help: consider reducing it to: `1` + +error: this operation has no effect + --> $DIR/identity_op.rs:118:5 + | +LL | 0 + if a { 1 } else { 2 } + if b { 3 } else { 5 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if a { 1 } else { 2 })` -error: aborting due to 33 previous errors +error: aborting due to 39 previous errors diff --git a/tests/ui/modulo_one.rs b/tests/ui/modulo_one.rs index 678a312f66e53..adff08e5d1e8b 100644 --- a/tests/ui/modulo_one.rs +++ b/tests/ui/modulo_one.rs @@ -1,5 +1,5 @@ #![warn(clippy::modulo_one)] -#![allow(clippy::no_effect, clippy::unnecessary_operation)] +#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::identity_op)] static STATIC_ONE: usize = 2 - 1; static STATIC_NEG_ONE: i64 = 1 - 2; diff --git a/tests/ui/modulo_one.stderr b/tests/ui/modulo_one.stderr index 03f460897fce2..04ecdef5e994e 100644 --- a/tests/ui/modulo_one.stderr +++ b/tests/ui/modulo_one.stderr @@ -38,14 +38,6 @@ error: any number modulo -1 will panic/overflow or result in 0 LL | i32::MIN % (-1); // also caught by rustc | ^^^^^^^^^^^^^^^ -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/modulo_one.rs:13:22 - | -LL | const ONE: u32 = 1 * 1; - | ^^^^^ - | - = note: `-D clippy::identity-op` implied by `-D warnings` - error: any number modulo 1 will be 0 --> $DIR/modulo_one.rs:17:5 | @@ -64,5 +56,5 @@ error: any number modulo -1 will panic/overflow or result in 0 LL | INT_MIN % NEG_ONE; // also caught by rustc | ^^^^^^^^^^^^^^^^^ -error: aborting due to 10 previous errors +error: aborting due to 9 previous errors From 5ad1b352063eda45f6fda7f3c2fd594e1b91aa41 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Tue, 10 May 2022 14:20:34 -0700 Subject: [PATCH 03/79] Update clippy to new rake_read signature --- clippy_lints/src/escape.rs | 2 +- clippy_lints/src/loops/mut_range_bound.rs | 2 +- clippy_lints/src/needless_pass_by_value.rs | 2 +- clippy_utils/src/sugg.rs | 2 +- clippy_utils/src/usage.rs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index af591dd71aa1d..807ecd2ddd16e 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -187,7 +187,7 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { } } - fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) {} + fn fake_read(&mut self, _: &rustc_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> { diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index 9d8679d77c6d0..d20df83045589 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -114,7 +114,7 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> { } } - fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) {} + fn fake_read(&mut self, _: &rustc_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } impl MutatePairDelegate<'_, '_> { diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 4034079a90c0d..303ce7a5075aa 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -343,5 +343,5 @@ impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt { fn mutate(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId) {} - fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) {} + fn fake_read(&mut self, _: &rustc_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 18915553e61c0..db5299c2c05f4 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -1033,7 +1033,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} - fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) {} + fn fake_read(&mut self, _: &rustc_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } #[cfg(test)] diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index 4236e3aae2fbd..b7b9d54d0b2c1 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -74,7 +74,7 @@ impl<'tcx> Delegate<'tcx> for MutVarsDelegate { self.update(cmt); } - fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) {} + fn fake_read(&mut self, _: &rustc_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } pub struct ParamBindingIdCollector { From 569505cebd16786dc748a6854773ddfd9311d891 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Thu, 19 May 2022 18:33:16 -0400 Subject: [PATCH 04/79] Auto-detect preferred colorscheme --- util/gh-pages/script.js | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/util/gh-pages/script.js b/util/gh-pages/script.js index bf4ce79b2cbc9..68e65acf9cb7e 100644 --- a/util/gh-pages/script.js +++ b/util/gh-pages/script.js @@ -343,17 +343,23 @@ function setTheme(theme, store) { let enableNight = false; let enableAyu = false; - if (theme == "ayu") { - enableAyu = true; - } else if (theme == "coal" || theme == "navy") { - enableNight = true; - } else if (theme == "rust") { - enableHighlight = true; - } else { - enableHighlight = true; - // this makes sure that an unknown theme request gets set to a known one - theme = "light"; + switch(theme) { + case "ayu": + enableAyu = true; + break; + case "coal": + case "navy": + enableNight = true; + break; + case "rust": + enableHighlight = true; + break; + default: + enableHighlight = true; + theme = "light"; + break; } + document.getElementsByTagName("body")[0].className = theme; document.getElementById("styleHighlight").disabled = !enableHighlight; @@ -368,4 +374,10 @@ function setTheme(theme, store) { } // loading the theme after the initial load -setTheme(localStorage.getItem('clippy-lint-list-theme'), false); +const prefersDark = window.matchMedia("(prefers-color-scheme: dark)"); +const theme = localStorage.getItem('clippy-lint-list-theme'); +if (prefersDark.matches && !theme) { + setTheme("coal", false); +} else { + setTheme(theme, false); +} From 4587b6628d1dff4a3c23ffcaadb7276d12f1e165 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 21 May 2022 13:24:00 +0200 Subject: [PATCH 05/79] Merge 'rust-clippy/master' into clippyup --- .github/deploy.sh | 1 + CHANGELOG.md | 45 +- CONTRIBUTING.md | 10 +- Cargo.toml | 3 +- clippy_dev/src/lint.rs | 5 +- clippy_dev/src/main.rs | 17 +- clippy_dev/src/new_lint.rs | 4 +- clippy_dev/src/update_lints.rs | 15 +- clippy_lints/Cargo.toml | 2 +- clippy_lints/src/approx_const.rs | 4 +- clippy_lints/src/assign_ops.rs | 2 +- clippy_lints/src/attrs.rs | 23 +- clippy_lints/src/bit_mask.rs | 40 +- clippy_lints/src/booleans.rs | 6 +- clippy_lints/src/borrow_as_ptr.rs | 2 +- .../src/casts/cast_abs_to_unsigned.rs | 4 +- clippy_lints/src/casts/cast_lossless.rs | 8 +- .../src/casts/cast_slice_different_sizes.rs | 6 +- clippy_lints/src/casts/mod.rs | 13 +- clippy_lints/src/casts/ptr_as_ptr.rs | 4 +- clippy_lints/src/checked_conversions.rs | 5 +- clippy_lints/src/cognitive_complexity.rs | 4 +- clippy_lints/src/collapsible_match.rs | 5 +- clippy_lints/src/dbg_macro.rs | 4 +- clippy_lints/src/default.rs | 2 +- clippy_lints/src/default_numeric_fallback.rs | 1 - .../src/default_union_representation.rs | 4 +- clippy_lints/src/deprecated_lints.rs | 1 - clippy_lints/src/dereference.rs | 4 +- clippy_lints/src/derive.rs | 79 ++- clippy_lints/src/disallowed_methods.rs | 14 +- clippy_lints/src/doc.rs | 6 +- clippy_lints/src/double_comparison.rs | 2 +- clippy_lints/src/duplicate_mod.rs | 102 ++++ clippy_lints/src/entry.rs | 4 +- clippy_lints/src/enum_clike.rs | 3 +- clippy_lints/src/enum_variants.rs | 2 +- clippy_lints/src/eq_op.rs | 3 +- clippy_lints/src/excessive_bools.rs | 2 - clippy_lints/src/fallible_impl_from.rs | 1 - clippy_lints/src/floating_point_arithmetic.rs | 2 +- clippy_lints/src/from_over_into.rs | 2 +- clippy_lints/src/if_then_some_else_none.rs | 2 +- clippy_lints/src/implicit_hasher.rs | 2 +- clippy_lints/src/implicit_return.rs | 4 +- clippy_lints/src/index_refutable_slice.rs | 3 +- clippy_lints/src/int_plus_one.rs | 2 +- clippy_lints/src/lib.register_all.rs | 7 +- clippy_lints/src/lib.register_complexity.rs | 2 +- clippy_lints/src/lib.register_lints.rs | 8 +- clippy_lints/src/lib.register_restriction.rs | 1 + clippy_lints/src/lib.register_style.rs | 1 + clippy_lints/src/lib.register_suspicious.rs | 4 +- clippy_lints/src/lib.rs | 26 +- clippy_lints/src/literal_representation.rs | 3 +- clippy_lints/src/loops/mod.rs | 1 - clippy_lints/src/loops/needless_range_loop.rs | 2 +- clippy_lints/src/loops/utils.rs | 2 +- .../src/loops/while_let_on_iterator.rs | 2 +- clippy_lints/src/macro_use.rs | 3 +- clippy_lints/src/main_recursion.rs | 2 +- clippy_lints/src/manual_bits.rs | 2 +- clippy_lints/src/manual_map.rs | 2 +- clippy_lints/src/manual_non_exhaustive.rs | 12 +- clippy_lints/src/manual_strip.rs | 2 +- clippy_lints/src/map_clone.rs | 2 +- clippy_lints/src/matches/match_same_arms.rs | 6 +- .../src/matches/match_single_binding.rs | 175 +++++-- clippy_lints/src/matches/match_wild_enum.rs | 3 +- clippy_lints/src/matches/mod.rs | 4 +- .../src/matches/redundant_pattern_match.rs | 2 +- clippy_lints/src/mem_replace.rs | 2 +- .../src/methods/cloned_instead_of_copied.rs | 6 +- clippy_lints/src/methods/err_expect.rs | 4 +- clippy_lints/src/methods/expect_used.rs | 7 +- clippy_lints/src/methods/filter_map_next.rs | 4 +- .../src/methods/is_digit_ascii_radix.rs | 4 +- clippy_lints/src/methods/map_unwrap_or.rs | 4 +- clippy_lints/src/methods/mod.rs | 376 +++++++------- .../src/methods/option_as_ref_deref.rs | 4 +- clippy_lints/src/methods/str_splitn.rs | 6 +- .../src/methods/unnecessary_to_owned.rs | 42 +- clippy_lints/src/methods/unwrap_used.rs | 7 +- clippy_lints/src/misc.rs | 54 +- clippy_lints/src/missing_const_for_fn.rs | 4 +- ...e.rs => mixed_read_write_in_expression.rs} | 8 +- clippy_lints/src/mutable_debug_assertion.rs | 2 +- clippy_lints/src/needless_bitwise_bool.rs | 4 +- clippy_lints/src/needless_pass_by_value.rs | 2 +- clippy_lints/src/neg_multiply.rs | 1 - clippy_lints/src/new_without_default.rs | 1 - clippy_lints/src/non_expressive_names.rs | 2 +- clippy_lints/src/pass_by_ref_or_value.rs | 4 +- clippy_lints/src/pattern_type_mismatch.rs | 1 - clippy_lints/src/ptr.rs | 2 +- clippy_lints/src/ranges.rs | 73 ++- clippy_lints/src/rc_clone_in_vec_init.rs | 141 ++++++ clippy_lints/src/redundant_clone.rs | 5 +- clippy_lints/src/redundant_field_names.rs | 2 +- .../src/redundant_static_lifetimes.rs | 2 +- clippy_lints/src/reference.rs | 5 +- clippy_lints/src/regex.rs | 2 - clippy_lints/src/renamed_lints.rs | 1 + .../src/significant_drop_in_scrutinee.rs | 28 +- clippy_lints/src/tabs_in_doc_comments.rs | 1 - clippy_lints/src/transmute/mod.rs | 4 +- .../src/types/redundant_allocation.rs | 7 +- .../src/undocumented_unsafe_blocks.rs | 196 ++++++-- clippy_lints/src/uninit_vec.rs | 2 +- clippy_lints/src/unit_types/let_unit_value.rs | 17 +- clippy_lints/src/unnested_or_patterns.rs | 8 +- clippy_lints/src/unused_unit.rs | 2 +- clippy_lints/src/use_self.rs | 6 +- clippy_lints/src/useless_conversion.rs | 2 +- clippy_lints/src/utils/conf.rs | 137 ++++- clippy_lints/src/vec.rs | 5 +- clippy_lints/src/vec_init_then_push.rs | 190 ++++--- clippy_lints/src/write.rs | 3 - clippy_utils/Cargo.toml | 2 +- clippy_utils/src/ast_utils.rs | 2 +- clippy_utils/src/attrs.rs | 4 +- clippy_utils/src/consts.rs | 13 +- clippy_utils/src/diagnostics.rs | 2 +- clippy_utils/src/eager_or_lazy.rs | 2 +- clippy_utils/src/higher.rs | 19 +- clippy_utils/src/hir_utils.rs | 8 +- clippy_utils/src/lib.rs | 38 +- clippy_utils/src/numeric_literal.rs | 2 +- clippy_utils/src/paths.rs | 22 +- clippy_utils/src/qualify_min_const_fn.rs | 8 +- clippy_utils/src/source.rs | 2 +- clippy_utils/src/sugg.rs | 5 +- clippy_utils/src/ty.rs | 2 +- clippy_utils/src/usage.rs | 1 - clippy_utils/src/visitors.rs | 60 ++- doc/adding_lints.md | 10 +- doc/common_tools_writing_lints.md | 26 +- lintcheck/Cargo.toml | 5 +- lintcheck/lintcheck_crates.toml | 2 +- lintcheck/src/config.rs | 140 ++++++ lintcheck/src/main.rs | 468 +++++------------- rust-toolchain | 2 +- src/main.rs | 4 + tests/dogfood.rs | 9 +- tests/ui-cargo/duplicate_mod/fail/Cargo.toml | 5 + tests/ui-cargo/duplicate_mod/fail/src/a.rs | 1 + tests/ui-cargo/duplicate_mod/fail/src/b.rs | 1 + tests/ui-cargo/duplicate_mod/fail/src/c.rs | 1 + .../fail/src/from_other_module.rs | 1 + tests/ui-cargo/duplicate_mod/fail/src/main.rs | 16 + .../duplicate_mod/fail/src/main.stderr | 42 ++ .../fail/src/other_module/mod.rs | 2 + tests/ui-toml/expect_used/clippy.toml | 1 + tests/ui-toml/expect_used/expect_used.rs | 29 ++ tests/ui-toml/expect_used/expect_used.stderr | 19 + .../conf_disallowed_methods.rs | 6 + .../conf_disallowed_methods.stderr | 20 +- .../toml_unknown_key/conf_unknown_key.stderr | 41 +- tests/ui-toml/unwrap_used/clippy.toml | 1 + tests/ui-toml/unwrap_used/unwrap_used.rs | 73 +++ tests/ui-toml/unwrap_used/unwrap_used.stderr | 197 ++++++++ tests/ui/absurd-extreme-comparisons.rs | 2 +- tests/ui/assign_ops2.rs | 2 +- .../proc_macro_suspicious_else_formatting.rs | 1 - tests/ui/blacklisted_name.rs | 4 +- .../ui/branches_sharing_code/shared_at_top.rs | 2 +- .../branches_sharing_code/valid_if_blocks.rs | 2 +- tests/ui/cast_size_32bit.stderr | 14 - tests/ui/checked_conversions.fixed | 2 - tests/ui/checked_conversions.rs | 2 - tests/ui/checked_conversions.stderr | 32 +- .../ui/cmp_owned/asymmetric_partial_eq.fixed | 2 +- tests/ui/cmp_owned/asymmetric_partial_eq.rs | 2 +- tests/ui/cmp_owned/with_suggestion.fixed | 4 +- tests/ui/cmp_owned/with_suggestion.rs | 4 +- tests/ui/cmp_owned/without_suggestion.rs | 26 +- tests/ui/cmp_owned/without_suggestion.stderr | 2 +- .../ui/crashes/auxiliary/proc_macro_crash.rs | 1 - tests/ui/crashes/ice-6254.rs | 1 + tests/ui/crashes/ice-6254.stderr | 2 +- tests/ui/crashes/ice-8821.rs | 8 + tests/ui/crashes/ice-8821.stderr | 10 + tests/ui/dbg_macro.rs | 12 + tests/ui/derive_hash_xor_eq.rs | 2 + tests/ui/derive_hash_xor_eq.stderr | 16 +- tests/ui/derive_partial_eq_without_eq.fixed | 98 ++++ tests/ui/derive_partial_eq_without_eq.rs | 98 ++++ tests/ui/derive_partial_eq_without_eq.stderr | 46 ++ tests/ui/equatable_if_let.fixed | 2 +- tests/ui/equatable_if_let.rs | 2 +- tests/ui/from_iter_instead_of_collect.fixed | 1 - tests/ui/from_iter_instead_of_collect.rs | 1 - tests/ui/from_iter_instead_of_collect.stderr | 30 +- tests/ui/get_unwrap.fixed | 1 - tests/ui/get_unwrap.rs | 1 - tests/ui/get_unwrap.stderr | 52 +- tests/ui/infinite_iter.rs | 1 - tests/ui/infinite_iter.stderr | 2 +- tests/ui/manual_str_repeat.fixed | 2 +- tests/ui/manual_str_repeat.rs | 2 +- tests/ui/map_err.rs | 1 - tests/ui/map_err.stderr | 2 +- tests/ui/match_single_binding.fixed | 13 + tests/ui/match_single_binding.rs | 14 + tests/ui/match_single_binding.stderr | 38 +- tests/ui/match_single_binding2.stderr | 4 +- tests/ui/methods.rs | 1 - tests/ui/methods.stderr | 4 +- ...e.rs => mixed_read_write_in_expression.rs} | 2 +- ... => mixed_read_write_in_expression.stderr} | 18 +- tests/ui/range_contains.fixed | 8 +- tests/ui/range_contains.rs | 8 +- tests/ui/range_contains.stderr | 14 +- tests/ui/rc_clone_in_vec_init/arc.rs | 68 +++ tests/ui/rc_clone_in_vec_init/arc.stderr | 109 ++++ tests/ui/rc_clone_in_vec_init/rc.rs | 69 +++ tests/ui/rc_clone_in_vec_init/rc.stderr | 109 ++++ tests/ui/recursive_format_impl.stderr | 11 +- tests/ui/redundant_allocation.rs | 35 ++ tests/ui/redundant_allocation.stderr | 38 +- tests/ui/rename.fixed | 2 + tests/ui/rename.rs | 2 + tests/ui/rename.stderr | 76 +-- tests/ui/self_assignment.rs | 2 +- tests/ui/significant_drop_in_scrutinee.rs | 159 +++--- tests/ui/significant_drop_in_scrutinee.stderr | 74 ++- tests/ui/undocumented_unsafe_blocks.rs | 153 +++++- tests/ui/undocumented_unsafe_blocks.stderr | 118 ++++- tests/ui/unit_cmp.rs | 6 +- tests/ui/unit_cmp.stderr | 12 +- tests/ui/unnecessary_to_owned.fixed | 61 ++- tests/ui/unnecessary_to_owned.rs | 61 ++- tests/ui/unnecessary_to_owned.stderr | 34 +- tests/ui/useless_conversion_try.rs | 2 - tests/ui/useless_conversion_try.stderr | 18 +- tests/ui/vec_init_then_push.rs | 60 +++ tests/ui/vec_init_then_push.stderr | 36 +- util/gh-pages/index.html | 396 ++++----------- util/gh-pages/script.js | 371 ++++++++++++++ 239 files changed, 4449 insertions(+), 1711 deletions(-) create mode 100644 clippy_lints/src/duplicate_mod.rs rename clippy_lints/src/{eval_order_dependence.rs => mixed_read_write_in_expression.rs} (98%) create mode 100644 clippy_lints/src/rc_clone_in_vec_init.rs create mode 100644 lintcheck/src/config.rs create mode 100644 tests/ui-cargo/duplicate_mod/fail/Cargo.toml create mode 100644 tests/ui-cargo/duplicate_mod/fail/src/a.rs create mode 100644 tests/ui-cargo/duplicate_mod/fail/src/b.rs create mode 100644 tests/ui-cargo/duplicate_mod/fail/src/c.rs create mode 100644 tests/ui-cargo/duplicate_mod/fail/src/from_other_module.rs create mode 100644 tests/ui-cargo/duplicate_mod/fail/src/main.rs create mode 100644 tests/ui-cargo/duplicate_mod/fail/src/main.stderr create mode 100644 tests/ui-cargo/duplicate_mod/fail/src/other_module/mod.rs create mode 100644 tests/ui-toml/expect_used/clippy.toml create mode 100644 tests/ui-toml/expect_used/expect_used.rs create mode 100644 tests/ui-toml/expect_used/expect_used.stderr create mode 100644 tests/ui-toml/unwrap_used/clippy.toml create mode 100644 tests/ui-toml/unwrap_used/unwrap_used.rs create mode 100644 tests/ui-toml/unwrap_used/unwrap_used.stderr create mode 100644 tests/ui/crashes/ice-8821.rs create mode 100644 tests/ui/crashes/ice-8821.stderr create mode 100644 tests/ui/derive_partial_eq_without_eq.fixed create mode 100644 tests/ui/derive_partial_eq_without_eq.rs create mode 100644 tests/ui/derive_partial_eq_without_eq.stderr rename tests/ui/{eval_order_dependence.rs => mixed_read_write_in_expression.rs} (97%) rename tests/ui/{eval_order_dependence.stderr => mixed_read_write_in_expression.stderr} (63%) create mode 100644 tests/ui/rc_clone_in_vec_init/arc.rs create mode 100644 tests/ui/rc_clone_in_vec_init/arc.stderr create mode 100644 tests/ui/rc_clone_in_vec_init/rc.rs create mode 100644 tests/ui/rc_clone_in_vec_init/rc.stderr create mode 100644 util/gh-pages/script.js diff --git a/.github/deploy.sh b/.github/deploy.sh index 34225a5402904..5a59f94ec918b 100644 --- a/.github/deploy.sh +++ b/.github/deploy.sh @@ -8,6 +8,7 @@ rm -rf out/master/ || exit 0 echo "Making the docs for master" mkdir out/master/ cp util/gh-pages/index.html out/master +cp util/gh-pages/script.js out/master cp util/gh-pages/lints.json out/master if [[ -n $TAG_NAME ]]; then diff --git a/CHANGELOG.md b/CHANGELOG.md index d25ad0ac6fa16..10ef00fcbafa5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,9 +8,9 @@ document. [d0cf3481...master](https://github.com/rust-lang/rust-clippy/compare/d0cf3481...master) -## Rust 1.61 (beta) +## Rust 1.61 -Current beta, released 2022-05-19 +Current stable, released 2022-05-19 [57b3c4b...d0cf3481](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...d0cf3481) @@ -111,7 +111,7 @@ Current beta, released 2022-05-19 ## Rust 1.60 -Current stable, released 2022-04-07 +Released 2022-04-07 [0eff589...57b3c4b](https://github.com/rust-lang/rust-clippy/compare/0eff589...57b3c4b) @@ -3290,6 +3290,8 @@ Released 2018-09-13 [`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map [`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name [`blanket_clippy_restriction_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#blanket_clippy_restriction_lints +[`block_in_if_condition_expr`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_expr +[`block_in_if_condition_stmt`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_stmt [`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions [`bool_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison [`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison @@ -3297,6 +3299,7 @@ Released 2018-09-13 [`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const [`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box [`box_collection`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_collection +[`box_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_vec [`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local [`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code [`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow @@ -3332,10 +3335,12 @@ Released 2018-09-13 [`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain [`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty +[`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime [`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator [`crate_in_macro_def`]: https://rust-lang.github.io/rust-clippy/master/index.html#crate_in_macro_def [`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir [`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute +[`cyclomatic_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cyclomatic_complexity [`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro [`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call [`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation @@ -3350,8 +3355,11 @@ Released 2018-09-13 [`derivable_impls`]: https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls [`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq [`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord +[`derive_partial_eq_without_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_partial_eq_without_eq +[`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method [`disallowed_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods [`disallowed_script_idents`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents +[`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type [`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown @@ -3359,9 +3367,11 @@ Released 2018-09-13 [`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use [`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg [`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens +[`drop_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_bounds [`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy [`drop_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_non_drop [`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref +[`duplicate_mod`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_mod [`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument [`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec [`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else @@ -3413,6 +3423,8 @@ Released 2018-09-13 [`fn_to_numeric_cast_any`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_any [`fn_to_numeric_cast_with_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_with_truncation [`for_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_kv_map +[`for_loop_over_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_option +[`for_loop_over_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_result [`for_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles [`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy [`forget_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_non_drop @@ -3425,9 +3437,11 @@ Released 2018-09-13 [`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send [`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len [`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap +[`identity_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_conversion [`identity_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_op [`if_let_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_mutex [`if_let_redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_redundant_pattern_matching +[`if_let_some_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_some_result [`if_not_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_not_else [`if_same_then_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else [`if_then_some_else_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none @@ -3456,8 +3470,11 @@ Released 2018-09-13 [`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one [`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic [`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division +[`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array [`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref +[`invalid_atomic_ordering`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_atomic_ordering [`invalid_null_ptr_usage`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_null_ptr_usage +[`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref [`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex [`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons [`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters @@ -3531,6 +3548,7 @@ Released 2018-09-13 [`match_wild_err_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wild_err_arm [`match_wildcard_for_single_variants`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wildcard_for_single_variants [`maybe_infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#maybe_infinite_iter +[`mem_discriminant_non_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_discriminant_non_enum [`mem_forget`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_forget [`mem_replace_option_with_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_option_with_none [`mem_replace_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default @@ -3549,6 +3567,7 @@ Released 2018-09-13 [`missing_spin_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_spin_loop [`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes [`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals +[`mixed_read_write_in_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_read_write_in_expression [`mod_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#mod_module_files [`module_inception`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_inception [`module_name_repetitions`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions @@ -3592,6 +3611,7 @@ Released 2018-09-13 [`never_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#never_loop [`new_ret_no_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_ret_no_self [`new_without_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default +[`new_without_default_derive`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default_derive [`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect [`no_effect_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_underscore_binding [`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal @@ -3605,19 +3625,25 @@ Released 2018-09-13 [`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect [`only_used_in_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#only_used_in_recursion [`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref +[`option_and_then_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_and_then_some [`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref [`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap +[`option_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_expect_used [`option_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_filter_map [`option_if_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else [`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none [`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn +[`option_map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or +[`option_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or_else [`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option +[`option_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_unwrap_used [`or_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call [`or_then_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_then_unwrap [`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing [`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional [`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic [`panic_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result_fn +[`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params [`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap [`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl [`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite @@ -3642,6 +3668,7 @@ Released 2018-09-13 [`range_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_step_by_zero [`range_zip_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_zip_with_len [`rc_buffer`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer +[`rc_clone_in_vec_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_clone_in_vec_init [`rc_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex [`recursive_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#recursive_format_impl [`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation @@ -3658,14 +3685,18 @@ Released 2018-09-13 [`redundant_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing [`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes [`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference +[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref [`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref [`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro [`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once [`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts [`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs +[`result_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_expect_used [`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option [`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn +[`result_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unwrap_or_else [`result_unit_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unit_err +[`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used [`return_self_not_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#return_self_not_must_use [`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition @@ -3684,10 +3715,12 @@ Released 2018-09-13 [`short_circuit_statement`]: https://rust-lang.github.io/rust-clippy/master/index.html#short_circuit_statement [`should_assert_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_assert_eq [`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait +[`significant_drop_in_scrutinee`]: https://rust-lang.github.io/rust-clippy/master/index.html#significant_drop_in_scrutinee [`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names [`single_char_add_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_add_str [`single_char_lifetime_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_lifetime_names [`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern +[`single_char_push_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_push_str [`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports [`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop [`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match @@ -3707,6 +3740,7 @@ Released 2018-09-13 [`string_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_to_string [`strlen_on_c_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#strlen_on_c_strings [`struct_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools +[`stutter`]: https://rust-lang.github.io/rust-clippy/master/index.html#stutter [`suboptimal_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#suboptimal_flops [`suspicious_arithmetic_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl [`suspicious_assignment_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_assignment_formatting @@ -3718,7 +3752,9 @@ Released 2018-09-13 [`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting [`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment +[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr [`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some +[`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display [`to_string_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_format_args [`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo [`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments @@ -3753,6 +3789,7 @@ Released 2018-09-13 [`unit_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_cmp [`unit_hash`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_hash [`unit_return_expecting_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_return_expecting_ord +[`unknown_clippy_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#unknown_clippy_lints [`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast [`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map [`unnecessary_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_find_map @@ -3782,6 +3819,7 @@ Released 2018-09-13 [`unused_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_async [`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect [`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount +[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit [`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings @@ -3822,5 +3860,6 @@ Released 2018-09-13 [`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal [`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr [`zero_sized_map_values`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_sized_map_values +[`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space [`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fc663de8f792d..6ab2bd59137fa 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -67,9 +67,9 @@ and resolved paths. [`T-AST`] issues will generally need you to match against a predefined syntax structure. To figure out how this syntax structure is encoded in the AST, it is recommended to run -`rustc -Z ast-json` on an example of the structure and compare with the [nodes in the AST docs]. +`rustc -Z unpretty=ast-tree` on an example of the structure and compare with the [nodes in the AST docs]. Usually the lint will end up to be a nested series of matches and ifs, [like so][deep-nesting]. -But we can make it nest-less by using [if_chain] macro, [like this][nest-less]. +But we can make it nest-less by using [let chains], [like this][nest-less]. [`E-medium`] issues are generally pretty easy too, though it's recommended you work on an [`good-first-issue`] first. Sometimes they are only somewhat involved code wise, but not difficult per-se. @@ -87,9 +87,9 @@ an AST expression). `match_def_path()` in Clippy's `utils` module can also be us [`E-medium`]: https://github.com/rust-lang/rust-clippy/labels/E-medium [`ty`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty [nodes in the AST docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/ -[deep-nesting]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/mem_forget.rs#L29-L43 -[if_chain]: https://docs.rs/if_chain/*/if_chain -[nest-less]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/bit_mask.rs#L124-L150 +[deep-nesting]: https://github.com/rust-lang/rust-clippy/blob/5e4f0922911536f80d9591180fa604229ac13939/clippy_lints/src/mem_forget.rs#L31-L45 +[let chains]: https://github.com/rust-lang/rust/pull/94927 +[nest-less]: https://github.com/rust-lang/rust-clippy/blob/5e4f0922911536f80d9591180fa604229ac13939/clippy_lints/src/bit_mask.rs#L133-L159 ## Writing code diff --git a/Cargo.toml b/Cargo.toml index dd6518d5241b5..373e720b0d5c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.62" +version = "0.1.63" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -25,6 +25,7 @@ clippy_lints = { path = "clippy_lints" } semver = "1.0" rustc_tools_util = { path = "rustc_tools_util" } tempfile = { version = "3.2", optional = true } +termize = "0.1" [dev-dependencies] compiletest_rs = { version = "0.7.1", features = ["tmp"] } diff --git a/clippy_dev/src/lint.rs b/clippy_dev/src/lint.rs index 1bc1a39542db5..9e463aa741c75 100644 --- a/clippy_dev/src/lint.rs +++ b/clippy_dev/src/lint.rs @@ -13,7 +13,7 @@ fn exit_if_err(status: io::Result) { } } -pub fn run(path: &str) { +pub fn run<'a>(path: &str, args: impl Iterator) { let is_file = match fs::metadata(path) { Ok(metadata) => metadata.is_file(), Err(e) => { @@ -30,6 +30,7 @@ pub fn run(path: &str) { .args(["-Z", "no-codegen"]) .args(["--edition", "2021"]) .arg(path) + .args(args) .status(), ); } else { @@ -42,6 +43,8 @@ pub fn run(path: &str) { .expect("failed to create tempdir"); let status = Command::new(cargo_clippy_path()) + .arg("clippy") + .args(args) .current_dir(path) .env("CARGO_TARGET_DIR", target.as_ref()) .status(); diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index ebf8f38d4906e..d5cd7ca96c0c0 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -76,7 +76,8 @@ fn main() { }, ("lint", Some(matches)) => { let path = matches.value_of("path").unwrap(); - lint::run(path); + let args = matches.values_of("args").into_iter().flatten(); + lint::run(path, args); }, ("rename_lint", Some(matches)) => { let old_name = matches.value_of("old_name").unwrap(); @@ -123,7 +124,7 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { * the lint count in README.md is correct\n \ * the changelog contains markdown link references at the bottom\n \ * all lint groups include the correct lints\n \ - * lint modules in `clippy_lints/*` are visible in `src/lifb.rs` via `pub mod`\n \ + * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod`\n \ * all lints are registered in the lint store", ) .arg(Arg::with_name("print-only").long("print-only").help( @@ -278,11 +279,23 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { Lint a package directory: cargo dev lint tests/ui-cargo/wildcard_dependencies/fail cargo dev lint ~/my-project + + Run rustfix: + cargo dev lint ~/my-project -- --fix + + Set lint levels: + cargo dev lint file.rs -- -W clippy::pedantic + cargo dev lint ~/my-project -- -- -W clippy::pedantic "}) .arg( Arg::with_name("path") .required(true) .help("The path to a file or package directory to lint"), + ) + .arg( + Arg::with_name("args") + .multiple(true) + .help("Pass extra arguments to cargo/clippy-driver"), ), ) .subcommand( diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 10f67d301f887..07d196387887c 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -133,7 +133,7 @@ fn to_camel_case(name: &str) -> String { .collect() } -fn get_stabilisation_version() -> String { +fn get_stabilization_version() -> String { fn parse_manifest(contents: &str) -> Option { let version = contents .lines() @@ -199,7 +199,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { }, }; - let version = get_stabilisation_version(); + let version = get_stabilization_version(); let lint_name = lint.name; let category = lint.category; let name_camel = to_camel_case(lint.name); diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 1a6a4336da27e..5024e63bfa738 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -17,7 +17,7 @@ const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev u const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html"; -#[derive(Clone, Copy, PartialEq)] +#[derive(Clone, Copy, PartialEq, Eq)] pub enum UpdateMode { Check, Change, @@ -66,8 +66,13 @@ fn generate_lint_files( |res| { for lint in usable_lints .iter() - .map(|l| &l.name) - .chain(deprecated_lints.iter().map(|l| &l.name)) + .map(|l| &*l.name) + .chain(deprecated_lints.iter().map(|l| &*l.name)) + .chain( + renamed_lints + .iter() + .map(|l| l.old_name.strip_prefix("clippy::").unwrap_or(&l.old_name)), + ) .sorted() { writeln!(res, "[`{}`]: {}#{}", lint, DOCS_LINK, lint).unwrap(); @@ -372,7 +377,7 @@ fn exit_with_failure() { } /// Lint data parsed from the Clippy source code. -#[derive(Clone, PartialEq, Debug)] +#[derive(Clone, PartialEq, Eq, Debug)] struct Lint { name: String, group: String, @@ -414,7 +419,7 @@ impl Lint { } } -#[derive(Clone, PartialEq, Debug)] +#[derive(Clone, PartialEq, Eq, Debug)] struct DeprecatedLint { name: String, reason: String, diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index aebf9a87cabd2..0a3f04da35705 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.62" +version = "0.1.63" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs index e109ee0009ee0..da1b646f4777a 100644 --- a/clippy_lints/src/approx_const.rs +++ b/clippy_lints/src/approx_const.rs @@ -87,9 +87,7 @@ impl ApproxConstant { let s = s.as_str(); if s.parse::().is_ok() { for &(constant, name, min_digits, msrv) in &KNOWN_CONSTS { - if is_approx_const(constant, s, min_digits) - && msrv.as_ref().map_or(true, |msrv| meets_msrv(self.msrv.as_ref(), msrv)) - { + if is_approx_const(constant, s, min_digits) && msrv.map_or(true, |msrv| meets_msrv(self.msrv, msrv)) { span_lint_and_help( cx, APPROX_CONSTANT, diff --git a/clippy_lints/src/assign_ops.rs b/clippy_lints/src/assign_ops.rs index 12c1bddf79d5d..4c2d3366483af 100644 --- a/clippy_lints/src/assign_ops.rs +++ b/clippy_lints/src/assign_ops.rs @@ -50,7 +50,7 @@ declare_clippy_lint! { /// ### Known problems /// Clippy cannot know for sure if `a op= a op b` should have /// been `a = a op a op b` or `a = a op b`/`a op= b`. Therefore, it suggests both. - /// If `a op= a op b` is really the correct behaviour it should be + /// If `a op= a op b` is really the correct behavior it should be /// written as `a = a op a op b` as it's less confusing. /// /// ### Example diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 8b0e11cb802ee..3de91f3d24a91 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -6,7 +6,7 @@ use clippy_utils::msrvs; use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments}; use clippy_utils::{extract_msrv_attr, meets_msrv}; use if_chain::if_chain; -use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MacArgs, MacArgsEq, MetaItemKind, NestedMetaItem}; +use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; use rustc_errors::Applicability; use rustc_hir::{ Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind, @@ -586,21 +586,10 @@ impl EarlyLintPass for EarlyAttributes { fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::Item) { for attr in &item.attrs { - let attr_item = if let AttrKind::Normal(ref attr, _) = attr.kind { - attr - } else { - return; - }; - - if attr.style == AttrStyle::Outer { - if let MacArgs::Eq(_, MacArgsEq::Ast(expr)) = &attr_item.args - && !matches!(expr.kind, rustc_ast::ExprKind::Lit(..)) { - return; - } - if attr_item.args.inner_tokens().is_empty() || !is_present_in_source(cx, attr.span) { - return; - } - + if matches!(attr.kind, AttrKind::Normal(..)) + && attr.style == AttrStyle::Outer + && is_present_in_source(cx, attr.span) + { let begin_of_attr_to_item = Span::new(attr.span.lo(), item.span.lo(), item.span.ctxt(), item.span.parent()); let end_of_attr_to_item = Span::new(attr.span.hi(), item.span.lo(), item.span.ctxt(), item.span.parent()); @@ -624,7 +613,7 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::It fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: Option) { if_chain! { - if meets_msrv(msrv.as_ref(), &msrvs::TOOL_ATTRIBUTES); + if meets_msrv(msrv, msrvs::TOOL_ATTRIBUTES); // check cfg_attr if attr.has_name(sym::cfg_attr); if let Some(items) = attr.meta_item_list(); diff --git a/clippy_lints/src/bit_mask.rs b/clippy_lints/src/bit_mask.rs index ca4af66cad16b..dc7e400fdc281 100644 --- a/clippy_lints/src/bit_mask.rs +++ b/clippy_lints/src/bit_mask.rs @@ -1,7 +1,6 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::sugg::Sugg; -use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; @@ -130,23 +129,24 @@ impl<'tcx> LateLintPass<'tcx> for BitMask { } } } - if_chain! { - if let ExprKind::Binary(op, left, right) = &e.kind; - if BinOpKind::Eq == op.node; - if let ExprKind::Binary(op1, left1, right1) = &left.kind; - if BinOpKind::BitAnd == op1.node; - if let ExprKind::Lit(lit) = &right1.kind; - if let LitKind::Int(n, _) = lit.node; - if let ExprKind::Lit(lit1) = &right.kind; - if let LitKind::Int(0, _) = lit1.node; - if n.leading_zeros() == n.count_zeros(); - if n > u128::from(self.verbose_bit_mask_threshold); - then { - span_lint_and_then(cx, - VERBOSE_BIT_MASK, - e.span, - "bit mask could be simplified with a call to `trailing_zeros`", - |diag| { + + if let ExprKind::Binary(op, left, right) = &e.kind + && BinOpKind::Eq == op.node + && let ExprKind::Binary(op1, left1, right1) = &left.kind + && BinOpKind::BitAnd == op1.node + && let ExprKind::Lit(lit) = &right1.kind + && let LitKind::Int(n, _) = lit.node + && let ExprKind::Lit(lit1) = &right.kind + && let LitKind::Int(0, _) = lit1.node + && n.leading_zeros() == n.count_zeros() + && n > u128::from(self.verbose_bit_mask_threshold) + { + span_lint_and_then( + cx, + VERBOSE_BIT_MASK, + e.span, + "bit mask could be simplified with a call to `trailing_zeros`", + |diag| { let sugg = Sugg::hir(cx, left1, "...").maybe_par(); diag.span_suggestion( e.span, @@ -154,8 +154,8 @@ impl<'tcx> LateLintPass<'tcx> for BitMask { format!("{}.trailing_zeros() >= {}", sugg, n.count_ones()), Applicability::MaybeIncorrect, ); - }); - } + }, + ); } } } diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index f7449c8dc72ed..0adb6327164e7 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -137,7 +137,7 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> { } for (n, expr) in self.terminals.iter().enumerate() { if eq_expr_value(self.cx, e, expr) { - #[allow(clippy::cast_possible_truncation)] + #[expect(clippy::cast_possible_truncation)] return Ok(Bool::Term(n as u8)); } @@ -149,7 +149,7 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> { if eq_expr_value(self.cx, e_lhs, expr_lhs); if eq_expr_value(self.cx, e_rhs, expr_rhs); then { - #[allow(clippy::cast_possible_truncation)] + #[expect(clippy::cast_possible_truncation)] return Ok(Bool::Not(Box::new(Bool::Term(n as u8)))); } } @@ -157,7 +157,7 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> { let n = self.terminals.len(); self.terminals.push(e); if n < 32 { - #[allow(clippy::cast_possible_truncation)] + #[expect(clippy::cast_possible_truncation)] Ok(Bool::Term(n as u8)) } else { Err("too many literals".to_owned()) diff --git a/clippy_lints/src/borrow_as_ptr.rs b/clippy_lints/src/borrow_as_ptr.rs index 9f8eb488c29ba..0993adbae2e6b 100644 --- a/clippy_lints/src/borrow_as_ptr.rs +++ b/clippy_lints/src/borrow_as_ptr.rs @@ -57,7 +57,7 @@ impl BorrowAsPtr { impl<'tcx> LateLintPass<'tcx> for BorrowAsPtr { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::BORROW_AS_PTR) { + if !meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) { return; } diff --git a/clippy_lints/src/casts/cast_abs_to_unsigned.rs b/clippy_lints/src/casts/cast_abs_to_unsigned.rs index e9b0f1f672de0..6bac6bf83f8e5 100644 --- a/clippy_lints/src/casts/cast_abs_to_unsigned.rs +++ b/clippy_lints/src/casts/cast_abs_to_unsigned.rs @@ -16,10 +16,10 @@ pub(super) fn check( cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, - msrv: &Option, + msrv: Option, ) { if_chain! { - if meets_msrv(msrv.as_ref(), &msrvs::UNSIGNED_ABS); + if meets_msrv(msrv, msrvs::UNSIGNED_ABS); if cast_from.is_integral(); if cast_to.is_integral(); if cast_from.is_signed(); diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index 4a95bed1148dc..938458e30cadc 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -16,7 +16,7 @@ pub(super) fn check( cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, - msrv: &Option, + msrv: Option, ) { if !should_lint(cx, expr, cast_from, cast_to, msrv) { return; @@ -68,7 +68,7 @@ fn should_lint( expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, - msrv: &Option, + msrv: Option, ) -> bool { // Do not suggest using From in consts/statics until it is valid to do so (see #2267). if in_constant(cx, expr.hir_id) { @@ -93,9 +93,9 @@ fn should_lint( } else { 64 }; - from_nbits < to_nbits + !is_isize_or_usize(cast_from) && from_nbits < to_nbits }, - (false, true) if matches!(cast_from.kind(), ty::Bool) && meets_msrv(msrv.as_ref(), &msrvs::FROM_BOOL) => true, + (false, true) if matches!(cast_from.kind(), ty::Bool) && meets_msrv(msrv, msrvs::FROM_BOOL) => true, (_, _) => { matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64)) }, diff --git a/clippy_lints/src/casts/cast_slice_different_sizes.rs b/clippy_lints/src/casts/cast_slice_different_sizes.rs index 2238668abca71..027c660ce3b24 100644 --- a/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -8,9 +8,9 @@ use rustc_semver::RustcVersion; use super::CAST_SLICE_DIFFERENT_SIZES; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Option) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Option) { // suggestion is invalid if `ptr::slice_from_raw_parts` does not exist - if !meets_msrv(msrv.as_ref(), &msrvs::PTR_SLICE_RAW_PARTS) { + if !meets_msrv(msrv, msrvs::PTR_SLICE_RAW_PARTS) { return; } @@ -121,7 +121,7 @@ fn expr_cast_chain_tys<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Optio let to_slice_ty = get_raw_slice_ty_mut(cast_to)?; // If the expression that makes up the source of this cast is itself a cast, recursively - // call `expr_cast_chain_tys` and update the end type with the final tartet type. + // call `expr_cast_chain_tys` and update the end type with the final target type. // Otherwise, this cast is not immediately nested, just construct the info for this cast if let Some(prev_info) = expr_cast_chain_tys(cx, cast_expr) { Some(CastChainInfo { diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index b58252dcb9424..daf3b7b4ce4fe 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -306,7 +306,7 @@ declare_clippy_lint! { /// Checks for casts of `&T` to `&mut T` anywhere in the code. /// /// ### Why is this bad? - /// It’s basically guaranteed to be undefined behaviour. + /// It’s basically guaranteed to be undefined behavior. /// `UnsafeCell` is the only way to obtain aliasable data that is considered /// mutable. /// @@ -413,6 +413,7 @@ declare_clippy_lint! { } declare_clippy_lint! { + /// ### What it does /// Checks for `as` casts between raw pointers to slices with differently sized elements. /// /// ### Why is this bad? @@ -531,7 +532,7 @@ impl_lint_pass!(Casts => [ impl<'tcx> LateLintPass<'tcx> for Casts { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if !in_external_macro(cx.sess(), expr.span) { - ptr_as_ptr::check(cx, expr, &self.msrv); + ptr_as_ptr::check(cx, expr, self.msrv); } if expr.span.from_expansion() { @@ -561,9 +562,9 @@ impl<'tcx> LateLintPass<'tcx> for Casts { cast_possible_wrap::check(cx, expr, cast_from, cast_to); cast_precision_loss::check(cx, expr, cast_from, cast_to); cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to); - cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); + cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv); } - cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); + cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv); cast_enum_constructor::check(cx, expr, cast_expr, cast_from); } } @@ -571,8 +572,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts { cast_ref_to_mut::check(cx, expr); cast_ptr_alignment::check(cx, expr); char_lit_as_u8::check(cx, expr); - ptr_as_ptr::check(cx, expr, &self.msrv); - cast_slice_different_sizes::check(cx, expr, &self.msrv); + ptr_as_ptr::check(cx, expr, self.msrv); + cast_slice_different_sizes::check(cx, expr, self.msrv); } extract_msrv_attr!(LateContext); diff --git a/clippy_lints/src/casts/ptr_as_ptr.rs b/clippy_lints/src/casts/ptr_as_ptr.rs index fb04f93fbcf97..46d45d09661ae 100644 --- a/clippy_lints/src/casts/ptr_as_ptr.rs +++ b/clippy_lints/src/casts/ptr_as_ptr.rs @@ -12,8 +12,8 @@ use rustc_semver::RustcVersion; use super::PTR_AS_PTR; -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option) { - if !meets_msrv(msrv.as_ref(), &msrvs::POINTER_CAST) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Option) { + if !meets_msrv(msrv, msrvs::POINTER_CAST) { return; } diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 31cc3698592b3..7eeaaa0192147 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -30,7 +30,6 @@ declare_clippy_lint! { /// Could be written: /// /// ```rust - /// # use std::convert::TryFrom; /// # let foo = 1; /// # let _ = /// i32::try_from(foo).is_ok() @@ -57,7 +56,7 @@ impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]); impl<'tcx> LateLintPass<'tcx> for CheckedConversions { fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::TRY_FROM) { + if !meets_msrv(self.msrv, msrvs::TRY_FROM) { return; } @@ -123,7 +122,7 @@ struct Conversion<'a> { } /// The kind of conversion that is checked -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] enum ConversionType { SignedToUnsigned, SignedToSigned, diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 2bf7f86890545..317c4bfb3226e 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -48,7 +48,7 @@ impl CognitiveComplexity { impl_lint_pass!(CognitiveComplexity => [COGNITIVE_COMPLEXITY]); impl CognitiveComplexity { - #[allow(clippy::cast_possible_truncation)] + #[expect(clippy::cast_possible_truncation)] fn check<'tcx>( &mut self, cx: &LateContext<'tcx>, @@ -70,7 +70,7 @@ impl CognitiveComplexity { let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym::Result) { returns } else { - #[allow(clippy::integer_division)] + #[expect(clippy::integer_division)] (returns / 2) }; diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/collapsible_match.rs index 826eb0ae6b13b..ec55009f347d3 100644 --- a/clippy_lints/src/collapsible_match.rs +++ b/clippy_lints/src/collapsible_match.rs @@ -109,7 +109,10 @@ 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(Let { init: e, .. }))| !is_local_used(cx, *e, binding_id)); + if outer_guard.map_or( + true, + |(Guard::If(e) | Guard::IfLet(Let { init: e, .. }))| !is_local_used(cx, *e, binding_id) + ); // ...or anywhere in the inner expression if match inner { IfLetOrMatch::IfLet(_, _, body, els) => { diff --git a/clippy_lints/src/dbg_macro.rs b/clippy_lints/src/dbg_macro.rs index a0e5d30263316..f99d793c201d7 100644 --- a/clippy_lints/src/dbg_macro.rs +++ b/clippy_lints/src/dbg_macro.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_in_test_function; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{is_in_cfg_test, is_in_test_function}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -37,7 +37,7 @@ impl LateLintPass<'_> for DbgMacro { let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; if cx.tcx.is_diagnostic_item(sym::dbg_macro, macro_call.def_id) { // we make an exception for test code - if is_in_test_function(cx.tcx, expr.hir_id) { + if is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id) { return; } let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index f7e4bc24321c5..243dfd3a46183 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -110,7 +110,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { } } - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn check_block(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) { // start from the `let mut _ = _::default();` and look at all the following // statements, see if they re-assign the fields of the binding diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index f3996e5b44d74..3d9f9ed41ce18 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -116,7 +116,6 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { } impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { - #[allow(clippy::too_many_lines)] fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { match &expr.kind { ExprKind::Call(func, args) => { diff --git a/clippy_lints/src/default_union_representation.rs b/clippy_lints/src/default_union_representation.rs index 9b5da0bd8a660..d559ad423df5f 100644 --- a/clippy_lints/src/default_union_representation.rs +++ b/clippy_lints/src/default_union_representation.rs @@ -25,7 +25,7 @@ declare_clippy_lint! { /// /// fn main() { /// let _x: u32 = unsafe { - /// Foo { a: 0_i32 }.b // Undefined behaviour: `b` is allowed to be padding + /// Foo { a: 0_i32 }.b // Undefined behavior: `b` is allowed to be padding /// }; /// } /// ``` @@ -39,7 +39,7 @@ declare_clippy_lint! { /// /// fn main() { /// let _x: u32 = unsafe { - /// Foo { a: 0_i32 }.b // Now defined behaviour, this is just an i32 -> u32 transmute + /// Foo { a: 0_i32 }.b // Now defined behavior, this is just an i32 -> u32 transmute /// }; /// } /// ``` diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index bba27576c8927..5d5ea0f49c8c8 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -194,7 +194,6 @@ declare_deprecated_lint! { /// ### Deprecation reason /// The `avoid_breaking_exported_api` config option was added, which /// enables the `enum_variant_names` lint for public items. - /// ``` #[clippy::version = "1.54.0"] pub PUB_ENUM_VARIANT_NAMES, "set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items" diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index fe3911983421b..ea4c0207bb013 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -168,7 +168,7 @@ struct RefPat { } impl<'tcx> LateLintPass<'tcx> for Dereferencing { - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Skip path expressions from deref calls. e.g. `Deref::deref(e)` if Some(expr.hir_id) == self.skip_expr.take() { @@ -580,7 +580,7 @@ fn find_adjustments<'tcx>( } } -#[allow(clippy::needless_pass_by_value)] +#[expect(clippy::needless_pass_by_value)] fn report(cx: &LateContext<'_>, expr: &Expr<'_>, state: State, data: StateData) { match state { State::DerefMethod { diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 545bc7d210332..fe99f4a8d55d1 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -1,8 +1,9 @@ -use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::paths; use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::{is_lint_allowed, match_def_path}; use if_chain::if_chain; +use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor}; use rustc_hir::{ BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, TraitRef, UnsafeSource, Unsafety, @@ -101,8 +102,8 @@ declare_clippy_lint! { /// types. /// /// ### Why is this bad? - /// To avoid surprising behaviour, these traits should - /// agree and the behaviour of `Copy` cannot be overridden. In almost all + /// To avoid surprising behavior, these traits should + /// agree and the behavior of `Copy` cannot be overridden. In almost all /// situations a `Copy` type should have a `Clone` implementation that does /// nothing more than copy the object, which is what `#[derive(Copy, Clone)]` /// gets you. @@ -156,11 +157,44 @@ declare_clippy_lint! { "deriving `serde::Deserialize` on a type that has methods using `unsafe`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for types that derive `PartialEq` and could implement `Eq`. + /// + /// ### Why is this bad? + /// If a type `T` derives `PartialEq` and all of its members implement `Eq`, + /// then `T` can always implement `Eq`. Implementing `Eq` allows `T` to be used + /// in APIs that require `Eq` types. It also allows structs containing `T` to derive + /// `Eq` themselves. + /// + /// ### Example + /// ```rust + /// #[derive(PartialEq)] + /// struct Foo { + /// i_am_eq: i32, + /// i_am_eq_too: Vec, + /// } + /// ``` + /// Use instead: + /// ```rust + /// #[derive(PartialEq, Eq)] + /// struct Foo { + /// i_am_eq: i32, + /// i_am_eq_too: Vec, + /// } + /// ``` + #[clippy::version = "1.62.0"] + pub DERIVE_PARTIAL_EQ_WITHOUT_EQ, + style, + "deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`" +} + declare_lint_pass!(Derive => [ EXPL_IMPL_CLONE_ON_COPY, DERIVE_HASH_XOR_EQ, DERIVE_ORD_XOR_PARTIAL_ORD, - UNSAFE_DERIVE_DESERIALIZE + UNSAFE_DERIVE_DESERIALIZE, + DERIVE_PARTIAL_EQ_WITHOUT_EQ ]); impl<'tcx> LateLintPass<'tcx> for Derive { @@ -171,14 +205,14 @@ impl<'tcx> LateLintPass<'tcx> for Derive { }) = item.kind { let ty = cx.tcx.type_of(item.def_id); - let is_automatically_derived = - cx.tcx.has_attr(item.def_id.to_def_id(), sym::automatically_derived); + let is_automatically_derived = cx.tcx.has_attr(item.def_id.to_def_id(), sym::automatically_derived); check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived); check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived); if is_automatically_derived { check_unsafe_derive_deserialize(cx, item, trait_ref, ty); + check_partial_eq_without_eq(cx, item.span, trait_ref, ty); } else { check_copy_clone(cx, item, trait_ref, ty); } @@ -419,3 +453,36 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { self.cx.tcx.hir() } } + +/// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint. +fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &TraitRef<'_>, ty: Ty<'tcx>) { + if_chain! { + if let ty::Adt(adt, substs) = ty.kind(); + if let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq); + if let Some(def_id) = trait_ref.trait_def_id(); + if cx.tcx.is_diagnostic_item(sym::PartialEq, def_id); + if !implements_trait(cx, ty, eq_trait_def_id, substs); + then { + // If all of our fields implement `Eq`, we can implement `Eq` too + for variant in adt.variants() { + for field in &variant.fields { + let ty = field.ty(cx.tcx, substs); + + if !implements_trait(cx, ty, eq_trait_def_id, substs) { + return; + } + } + } + + span_lint_and_sugg( + cx, + DERIVE_PARTIAL_EQ_WITHOUT_EQ, + span.ctxt().outer_expn_data().call_site, + "you are deriving `PartialEq` and can implement `Eq`", + "consider deriving `Eq` as well", + "PartialEq, Eq".to_string(), + Applicability::MachineApplicable, + ) + } + } +} diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs index 4c12202c84ab3..53973ab792a91 100644 --- a/clippy_lints/src/disallowed_methods.rs +++ b/clippy_lints/src/disallowed_methods.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::fn_def_id; +use clippy_utils::{fn_def_id, get_parent_expr, path_def_id}; -use rustc_hir::{def::Res, def_id::DefIdMap, Expr}; +use rustc_hir::{def::Res, def_id::DefIdMap, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -84,7 +84,15 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let def_id = match fn_def_id(cx, expr) { + let uncalled_path = if let Some(parent) = get_parent_expr(cx, expr) + && let ExprKind::Call(receiver, _) = parent.kind + && receiver.hir_id == expr.hir_id + { + None + } else { + path_def_id(cx, expr) + }; + let def_id = match uncalled_path.or_else(|| fn_def_id(cx, expr)) { Some(def_id) => def_id, None => return, }; diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index b3fd8af4730dc..aaec88f50c771 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -198,7 +198,7 @@ declare_clippy_lint! { "presence of `fn main() {` in code examples" } -#[allow(clippy::module_name_repetitions)] +#[expect(clippy::module_name_repetitions)] #[derive(Clone)] pub struct DocMarkdown { valid_idents: FxHashSet, @@ -373,7 +373,7 @@ fn lint_for_missing_headers<'tcx>( /// `rustc_ast::parse::lexer::comments::strip_doc_comment_decoration` because we /// need to keep track of /// the spans but this function is inspired from the later. -#[allow(clippy::cast_possible_truncation)] +#[expect(clippy::cast_possible_truncation)] #[must_use] pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span: Span) -> (String, Vec<(usize, Span)>) { // one-line comments lose their prefix @@ -428,7 +428,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs /// We don't want the parser to choke on intra doc links. Since we don't /// actually care about rendering them, just pretend that all broken links are /// point to a fake address. - #[allow(clippy::unnecessary_wraps)] // we're following a type signature + #[expect(clippy::unnecessary_wraps)] // we're following a type signature fn fake_broken_link_callback<'a>(_: BrokenLink<'_>) -> Option<(CowStr<'a>, CowStr<'a>)> { Some(("fake".into(), "fake".into())) } diff --git a/clippy_lints/src/double_comparison.rs b/clippy_lints/src/double_comparison.rs index 176092e5b2800..be95375789d5b 100644 --- a/clippy_lints/src/double_comparison.rs +++ b/clippy_lints/src/double_comparison.rs @@ -40,7 +40,7 @@ declare_clippy_lint! { declare_lint_pass!(DoubleComparisons => [DOUBLE_COMPARISONS]); impl<'tcx> DoubleComparisons { - #[allow(clippy::similar_names)] + #[expect(clippy::similar_names)] fn check_binop(cx: &LateContext<'tcx>, op: BinOpKind, lhs: &'tcx Expr<'_>, rhs: &'tcx Expr<'_>, span: Span) { let (lkind, llhs, lrhs, rkind, rlhs, rrhs) = match (&lhs.kind, &rhs.kind) { (ExprKind::Binary(lb, llhs, lrhs), ExprKind::Binary(rb, rlhs, rrhs)) => { diff --git a/clippy_lints/src/duplicate_mod.rs b/clippy_lints/src/duplicate_mod.rs new file mode 100644 index 0000000000000..c6c7b959d4f49 --- /dev/null +++ b/clippy_lints/src/duplicate_mod.rs @@ -0,0 +1,102 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_ast::ast::{Crate, Inline, Item, ItemKind, ModKind}; +use rustc_errors::MultiSpan; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{FileName, Span}; +use std::collections::BTreeMap; +use std::path::PathBuf; + +declare_clippy_lint! { + /// ### What it does + /// Checks for files that are included as modules multiple times. + /// + /// ### Why is this bad? + /// Loading a file as a module more than once causes it to be compiled + /// multiple times, taking longer and putting duplicate content into the + /// module tree. + /// + /// ### Example + /// ```rust,ignore + /// // lib.rs + /// mod a; + /// mod b; + /// ``` + /// ```rust,ignore + /// // a.rs + /// #[path = "./b.rs"] + /// mod b; + /// ``` + /// + /// Use instead: + /// + /// ```rust,ignore + /// // lib.rs + /// mod a; + /// mod b; + /// ``` + /// ```rust,ignore + /// // a.rs + /// use crate::b; + /// ``` + #[clippy::version = "1.62.0"] + pub DUPLICATE_MOD, + suspicious, + "file loaded as module multiple times" +} + +#[derive(PartialOrd, Ord, PartialEq, Eq)] +struct Modules { + local_path: PathBuf, + spans: Vec, +} + +#[derive(Default)] +pub struct DuplicateMod { + /// map from the canonicalized path to `Modules`, `BTreeMap` to make the + /// order deterministic for tests + modules: BTreeMap, +} + +impl_lint_pass!(DuplicateMod => [DUPLICATE_MOD]); + +impl EarlyLintPass for DuplicateMod { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if let ItemKind::Mod(_, ModKind::Loaded(_, Inline::No, mod_spans)) = &item.kind + && let FileName::Real(real) = cx.sess().source_map().span_to_filename(mod_spans.inner_span) + && let Some(local_path) = real.into_local_path() + && let Ok(absolute_path) = local_path.canonicalize() + { + let modules = self.modules.entry(absolute_path).or_insert(Modules { + local_path, + spans: Vec::new(), + }); + modules.spans.push(item.span_with_attributes()); + } + } + + fn check_crate_post(&mut self, cx: &EarlyContext<'_>, _: &Crate) { + for Modules { local_path, spans } in self.modules.values() { + if spans.len() < 2 { + continue; + } + + let mut multi_span = MultiSpan::from_spans(spans.clone()); + let (&first, duplicates) = spans.split_first().unwrap(); + + multi_span.push_span_label(first, "first loaded here"); + for &duplicate in duplicates { + multi_span.push_span_label(duplicate, "loaded again here"); + } + + span_lint_and_help( + cx, + DUPLICATE_MOD, + multi_span, + &format!("file is loaded as a module multiple times: `{}`", local_path.display()), + None, + "replace all but one `mod` item with `use` items", + ); + } + } +} diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index d3d3ed2c2357e..c5a987842c3f1 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -63,7 +63,7 @@ declare_clippy_lint! { declare_lint_pass!(HashMapPass => [MAP_ENTRY]); impl<'tcx> LateLintPass<'tcx> for HashMapPass { - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let (cond_expr, then_expr, else_expr) = match higher::If::hir(expr) { Some(higher::If { cond, then, r#else }) => (cond, then, r#else), @@ -319,7 +319,7 @@ struct Insertion<'tcx> { /// `or_insert_with`. /// * Determine if there's any sub-expression that can't be placed in a closure. /// * Determine if there's only a single insert statement. `or_insert` can be used in this case. -#[allow(clippy::struct_excessive_bools)] +#[expect(clippy::struct_excessive_bools)] struct InsertSearcher<'cx, 'tcx> { cx: &'cx LateContext<'tcx>, /// The map expression used in the contains call. diff --git a/clippy_lints/src/enum_clike.rs b/clippy_lints/src/enum_clike.rs index e2a5430da08c8..10be245b36293 100644 --- a/clippy_lints/src/enum_clike.rs +++ b/clippy_lints/src/enum_clike.rs @@ -8,7 +8,6 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::{self, IntTy, UintTy}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use std::convert::TryFrom; declare_clippy_lint! { /// ### What it does @@ -37,7 +36,7 @@ declare_clippy_lint! { declare_lint_pass!(UnportableVariant => [ENUM_CLIKE_UNPORTABLE_VARIANT]); impl<'tcx> LateLintPass<'tcx> for UnportableVariant { - #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap, clippy::cast_sign_loss)] + #[expect(clippy::cast_possible_wrap)] fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if cx.tcx.data_layout.pointer_size.bits() != 64 { return; diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index 346d03ca5568f..e029b8e85379f 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -240,7 +240,7 @@ impl LateLintPass<'_> for EnumVariantNames { assert!(last.is_some()); } - #[allow(clippy::similar_names)] + #[expect(clippy::similar_names)] fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { let item_name = item.ident.name.as_str(); let item_camel = to_camel_case(item_name); diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index 51c811b304cae..afb5d32f95334 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -72,7 +72,7 @@ declare_clippy_lint! { declare_lint_pass!(EqOp => [EQ_OP, OP_REF]); impl<'tcx> LateLintPass<'tcx> for EqOp { - #[allow(clippy::similar_names, clippy::too_many_lines)] + #[expect(clippy::similar_names, clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if_chain! { if let Some((macro_call, macro_name)) = first_node_macro_backtrace(cx, e).find_map(|macro_call| { @@ -138,7 +138,6 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { }, }; if let Some(trait_id) = trait_id { - #[allow(clippy::match_same_arms)] match (&left.kind, &right.kind) { // do not suggest to dereference literals (&ExprKind::Lit(..), _) | (_, &ExprKind::Lit(..)) => {}, diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 245a4fc12fd4c..7a81fb37e841c 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -4,8 +4,6 @@ use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; -use std::convert::TryInto; - declare_clippy_lint! { /// ### What it does /// Checks for excessive diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index 088d9996516e8..9f868df3ad063 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -32,7 +32,6 @@ declare_clippy_lint! { /// // Good /// struct Foo(i32); /// - /// use std::convert::TryFrom; /// impl TryFrom for Foo { /// type Error = (); /// fn try_from(s: String) -> Result { diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 79ce53f7a5f23..42503c26de1d1 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -215,7 +215,7 @@ fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { // converted to an integer without loss of precision. For now we only check // ranges [-16777215, 16777216) for type f32 as whole number floats outside // this range are lossy and ambiguous. -#[allow(clippy::cast_possible_truncation)] +#[expect(clippy::cast_possible_truncation)] fn get_integer_from_float_constant(value: &Constant) -> Option { match value { F32(num) if num.fract() == 0.0 => { diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index c2f52605151ed..5d25c1d06341f 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -55,7 +55,7 @@ impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]); impl<'tcx> LateLintPass<'tcx> for FromOverInto { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::RE_REBALANCING_COHERENCE) { + if !meets_msrv(self.msrv, msrvs::RE_REBALANCING_COHERENCE) { return; } diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 9525c163ece17..b8d227855d976 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -57,7 +57,7 @@ impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]); impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::BOOL_THEN) { + if !meets_msrv(self.msrv, msrvs::BOOL_THEN) { return; } diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index feb1b1014b180..4f9680f60fe8b 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -62,7 +62,7 @@ declare_clippy_lint! { declare_lint_pass!(ImplicitHasher => [IMPLICIT_HASHER]); impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { - #[allow(clippy::cast_possible_truncation, clippy::too_many_lines)] + #[expect(clippy::cast_possible_truncation, clippy::too_many_lines)] fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { use rustc_span::BytePos; diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index d650d6e9a8587..647947d5d30d6 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -164,7 +164,7 @@ fn lint_implicit_returns( }) .visit_block(block); if add_return { - #[allow(clippy::option_if_let_else)] + #[expect(clippy::option_if_let_else)] if let Some(span) = call_site_span { lint_return(cx, span); LintLocation::Parent @@ -196,7 +196,7 @@ fn lint_implicit_returns( _ => { - #[allow(clippy::option_if_let_else)] + #[expect(clippy::option_if_let_else)] if let Some(span) = call_site_span { lint_return(cx, span); LintLocation::Parent diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 8a84513b7792f..9ce5b8e17a9ae 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -14,7 +14,6 @@ use rustc_middle::ty; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{symbol::Ident, Span}; -use std::convert::TryInto; declare_clippy_lint! { /// ### What it does @@ -75,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice { if !expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some(); if let Some(IfLet {let_pat, if_then, ..}) = IfLet::hir(cx, expr); if !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id); - if meets_msrv(self.msrv.as_ref(), &msrvs::SLICE_PATTERNS); + if meets_msrv(self.msrv, msrvs::SLICE_PATTERNS); let found_slices = find_slice_values(cx, let_pat); if !found_slices.is_empty(); diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs index 3716d36ad8816..8db7b307ddb75 100644 --- a/clippy_lints/src/int_plus_one.rs +++ b/clippy_lints/src/int_plus_one.rs @@ -52,7 +52,7 @@ enum Side { } impl IntPlusOne { - #[allow(clippy::cast_sign_loss)] + #[expect(clippy::cast_sign_loss)] fn check_lit(lit: &Lit, target_value: i128) -> bool { if let LitKind::Int(value, ..) = lit.kind { return value == (target_value as u128); diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index e68718f9fdf99..be5c478900fac 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -46,6 +46,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(derivable_impls::DERIVABLE_IMPLS), LintId::of(derive::DERIVE_HASH_XOR_EQ), LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD), + LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ), LintId::of(disallowed_methods::DISALLOWED_METHODS), LintId::of(disallowed_types::DISALLOWED_TYPES), LintId::of(doc::MISSING_SAFETY_DOC), @@ -59,6 +60,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(drop_forget_ref::FORGET_NON_DROP), LintId::of(drop_forget_ref::FORGET_REF), LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS), + LintId::of(duplicate_mod::DUPLICATE_MOD), LintId::of(duration_subsec::DURATION_SUBSEC), LintId::of(entry::MAP_ENTRY), LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), @@ -69,8 +71,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(erasing_op::ERASING_OP), LintId::of(escape::BOXED_LOCAL), LintId::of(eta_reduction::REDUNDANT_CLOSURE), - LintId::of(eval_order_dependence::DIVERGING_SUB_EXPRESSION), - LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE), LintId::of(explicit_write::EXPLICIT_WRITE), LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), LintId::of(float_literal::EXCESSIVE_PRECISION), @@ -228,6 +228,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(misc_early::REDUNDANT_PATTERN), LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN), LintId::of(misc_early::ZERO_PREFIXED_LITERAL), + LintId::of(mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION), LintId::of(mut_key::MUTABLE_KEY_TYPE), LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK), LintId::of(mut_reference::UNNECESSARY_MUT_PASSED), @@ -263,6 +264,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(ranges::MANUAL_RANGE_CONTAINS), LintId::of(ranges::RANGE_ZIP_WITH_LEN), LintId::of(ranges::REVERSED_EMPTY_RANGES), + LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT), LintId::of(redundant_clone::REDUNDANT_CLONE), LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL), LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES), @@ -276,6 +278,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(self_assignment::SELF_ASSIGNMENT), LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS), LintId::of(serde_api::SERDE_API_MISUSE), + LintId::of(significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE), LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs index 6f3c433af31a6..b15c979d0c70e 100644 --- a/clippy_lints/src/lib.register_complexity.rs +++ b/clippy_lints/src/lib.register_complexity.rs @@ -12,7 +12,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(double_comparison::DOUBLE_COMPARISONS), LintId::of(double_parens::DOUBLE_PARENS), LintId::of(duration_subsec::DURATION_SUBSEC), - LintId::of(eval_order_dependence::DIVERGING_SUB_EXPRESSION), LintId::of(explicit_write::EXPLICIT_WRITE), LintId::of(format::USELESS_FORMAT), LintId::of(functions::TOO_MANY_ARGUMENTS), @@ -59,6 +58,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(misc::SHORT_CIRCUIT_STATEMENT), LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN), LintId::of(misc_early::ZERO_PREFIXED_LITERAL), + LintId::of(mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION), LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE), LintId::of(needless_bool::BOOL_COMPARISON), LintId::of(needless_bool::NEEDLESS_BOOL), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index c888a5feda2ae..3350697ed7a41 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -113,6 +113,7 @@ store.register_lints(&[ derivable_impls::DERIVABLE_IMPLS, derive::DERIVE_HASH_XOR_EQ, derive::DERIVE_ORD_XOR_PARTIAL_ORD, + derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ, derive::EXPL_IMPL_CLONE_ON_COPY, derive::UNSAFE_DERIVE_DESERIALIZE, disallowed_methods::DISALLOWED_METHODS, @@ -132,6 +133,7 @@ store.register_lints(&[ drop_forget_ref::FORGET_NON_DROP, drop_forget_ref::FORGET_REF, drop_forget_ref::UNDROPPED_MANUALLY_DROPS, + duplicate_mod::DUPLICATE_MOD, duration_subsec::DURATION_SUBSEC, else_if_without_else::ELSE_IF_WITHOUT_ELSE, empty_drop::EMPTY_DROP, @@ -149,8 +151,6 @@ store.register_lints(&[ escape::BOXED_LOCAL, eta_reduction::REDUNDANT_CLOSURE, eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS, - eval_order_dependence::DIVERGING_SUB_EXPRESSION, - eval_order_dependence::EVAL_ORDER_DEPENDENCE, excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS, excessive_bools::STRUCT_EXCESSIVE_BOOLS, exhaustive_items::EXHAUSTIVE_ENUMS, @@ -381,6 +381,8 @@ store.register_lints(&[ missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS, missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES, missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS, + mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION, + mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION, module_style::MOD_MODULE_FILES, module_style::SELF_NAMED_MODULE_FILES, modulo_arithmetic::MODULO_ARITHMETIC, @@ -446,6 +448,7 @@ store.register_lints(&[ ranges::RANGE_PLUS_ONE, ranges::RANGE_ZIP_WITH_LEN, ranges::REVERSED_EMPTY_RANGES, + rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT, redundant_clone::REDUNDANT_CLONE, redundant_closure_call::REDUNDANT_CLOSURE_CALL, redundant_else::REDUNDANT_ELSE, @@ -470,6 +473,7 @@ store.register_lints(&[ shadow::SHADOW_REUSE, shadow::SHADOW_SAME, shadow::SHADOW_UNRELATED, + significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE, single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES, single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs index 77ec6c83ba4b4..a6d3a06dc16e3 100644 --- a/clippy_lints/src/lib.register_restriction.rs +++ b/clippy_lints/src/lib.register_restriction.rs @@ -46,6 +46,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS), LintId::of(missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES), LintId::of(missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS), + LintId::of(mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION), LintId::of(module_style::MOD_MODULE_FILES), LintId::of(module_style::SELF_NAMED_MODULE_FILES), LintId::of(modulo_arithmetic::MODULO_ARITHMETIC), diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index d183c0449cd5f..62f26d821a0d6 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -16,6 +16,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(comparison_chain::COMPARISON_CHAIN), LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), LintId::of(dereference::NEEDLESS_BORROW), + LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ), LintId::of(disallowed_methods::DISALLOWED_METHODS), LintId::of(disallowed_types::DISALLOWED_TYPES), LintId::of(doc::MISSING_SAFETY_DOC), diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs index 7a881bfe39912..2de49f1624a42 100644 --- a/clippy_lints/src/lib.register_suspicious.rs +++ b/clippy_lints/src/lib.register_suspicious.rs @@ -14,7 +14,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF), LintId::of(drop_forget_ref::DROP_NON_DROP), LintId::of(drop_forget_ref::FORGET_NON_DROP), - LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE), + LintId::of(duplicate_mod::DUPLICATE_MOD), LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), LintId::of(format_impl::PRINT_IN_FORMAT_IMPL), LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), @@ -26,6 +26,8 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(methods::SUSPICIOUS_MAP), LintId::of(mut_key::MUTABLE_KEY_TYPE), LintId::of(octal_escapes::OCTAL_ESCAPES), + LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT), + LintId::of(significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE), LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), ]) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 09071a255c5e6..4ac834f72405b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -8,6 +8,7 @@ #![feature(iter_intersperse)] #![feature(let_chains)] #![feature(let_else)] +#![feature(lint_reasons)] #![feature(once_cell)] #![feature(rustc_private)] #![feature(stmt_expr_attributes)] @@ -210,6 +211,7 @@ mod doc; mod double_comparison; mod double_parens; mod drop_forget_ref; +mod duplicate_mod; mod duration_subsec; mod else_if_without_else; mod empty_drop; @@ -223,7 +225,6 @@ mod equatable_if_let; mod erasing_op; mod escape; mod eta_reduction; -mod eval_order_dependence; mod excessive_bools; mod exhaustive_items; mod exit; @@ -299,6 +300,7 @@ mod missing_const_for_fn; mod missing_doc; mod missing_enforced_import_rename; mod missing_inline; +mod mixed_read_write_in_expression; mod module_style; mod modulo_arithmetic; mod mut_key; @@ -345,6 +347,7 @@ mod ptr_offset_with_cast; mod pub_use; mod question_mark; mod ranges; +mod rc_clone_in_vec_init; mod redundant_clone; mod redundant_closure_call; mod redundant_else; @@ -417,7 +420,7 @@ mod zero_sized_map_values; // end lints modules, do not remove this comment, it’s used in `update_lints` pub use crate::utils::conf::Conf; -use crate::utils::conf::TryConf; +use crate::utils::conf::{format_error, TryConf}; /// Register all pre expansion lints /// @@ -462,7 +465,7 @@ pub fn read_conf(sess: &Session) -> Conf { sess.struct_err(&format!( "error reading Clippy's configuration file `{}`: {}", file_name.display(), - error + format_error(error) )) .emit(); } @@ -473,7 +476,7 @@ pub fn read_conf(sess: &Session) -> Conf { /// Register all lints and lint groups with the rustc plugin registry /// /// Used in `./src/driver.rs`. -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) { register_removed_non_tool_lints(store); @@ -583,8 +586,17 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: }); let avoid_breaking_exported_api = conf.avoid_breaking_exported_api; + let allow_expect_in_tests = conf.allow_expect_in_tests; + let allow_unwrap_in_tests = conf.allow_unwrap_in_tests; store.register_late_pass(move || Box::new(approx_const::ApproxConstant::new(msrv))); - store.register_late_pass(move || Box::new(methods::Methods::new(avoid_breaking_exported_api, msrv))); + store.register_late_pass(move || { + Box::new(methods::Methods::new( + avoid_breaking_exported_api, + msrv, + allow_expect_in_tests, + allow_unwrap_in_tests, + )) + }); store.register_late_pass(move || Box::new(matches::Matches::new(msrv))); store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv))); store.register_late_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv))); @@ -669,7 +681,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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(mixed_read_write_in_expression::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)); @@ -892,6 +904,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let max_include_file_size = conf.max_include_file_size; store.register_late_pass(move || Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size))); store.register_late_pass(|| Box::new(strings::TrimSplitWhitespace)); + store.register_late_pass(|| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)); + store.register_early_pass(|| Box::new(duplicate_mod::DuplicateMod::default())); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 269d3c62eafcd..9998712b8527d 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -204,7 +204,6 @@ impl WarningType { } } -#[allow(clippy::module_name_repetitions)] #[derive(Copy, Clone)] pub struct LiteralDigitGrouping { lint_fraction_readability: bool, @@ -432,7 +431,7 @@ impl LiteralDigitGrouping { } } -#[allow(clippy::module_name_repetitions)] +#[expect(clippy::module_name_repetitions)] #[derive(Copy, Clone)] pub struct DecimalLiteralRepresentation { threshold: u64, diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index f029067d36715..75d771f992a8c 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -620,7 +620,6 @@ 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<'_>) { let for_loop = higher::ForLoop::hir(expr); if let Some(higher::ForLoop { diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 6ed141fa4a5a6..09f9c05b4fced 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -19,7 +19,7 @@ use std::mem; /// Checks for looping over a range and then indexing a sequence with it. /// The iteratee must be a range literal. -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index 772d251b620a8..4801a84eb92ce 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -13,7 +13,7 @@ use rustc_span::symbol::{sym, Symbol}; use rustc_typeck::hir_ty_to_ty; use std::iter::Iterator; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] enum IncrementVisitorVarState { Initial, // Not examined yet IncrOnce, // Incremented exactly once, may be a loop counter diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index 20a8294a0d1ac..82760607ba295 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -239,7 +239,7 @@ fn uses_iter<'tcx>(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tc v.uses_iter } -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: &Expr<'_>) -> bool { struct AfterLoopVisitor<'a, 'b, 'tcx> { cx: &'a LateContext<'tcx>, diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index ca1ccb93bf3fb..753469c603d40 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -48,7 +48,7 @@ impl MacroRefData { } #[derive(Default)] -#[allow(clippy::module_name_repetitions)] +#[expect(clippy::module_name_repetitions)] pub struct MacroUseImports { /// the actual import path used and the span of the attribute above it. imports: Vec<(String, Span)>, @@ -134,7 +134,6 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { self.push_unique_macro_pat_ty(cx, ty.span); } } - #[allow(clippy::too_many_lines)] fn check_crate_post(&mut self, cx: &LateContext<'_>) { let mut used = FxHashMap::default(); let mut check_dup = vec![]; diff --git a/clippy_lints/src/main_recursion.rs b/clippy_lints/src/main_recursion.rs index fad8fa467d4b8..20333c150e3d5 100644 --- a/clippy_lints/src/main_recursion.rs +++ b/clippy_lints/src/main_recursion.rs @@ -12,7 +12,7 @@ declare_clippy_lint! { /// /// ### Why is this bad? /// Apart from special setups (which we could detect following attributes like #![no_std]), - /// recursing into main() seems like an unintuitive antipattern we should be able to detect. + /// recursing into main() seems like an unintuitive anti-pattern we should be able to detect. /// /// ### Example /// ```no_run diff --git a/clippy_lints/src/manual_bits.rs b/clippy_lints/src/manual_bits.rs index ac3d9447b6bd3..60bbcde4f1de5 100644 --- a/clippy_lints/src/manual_bits.rs +++ b/clippy_lints/src/manual_bits.rs @@ -48,7 +48,7 @@ impl_lint_pass!(ManualBits => [MANUAL_BITS]); impl<'tcx> LateLintPass<'tcx> for ManualBits { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::MANUAL_BITS) { + if !meets_msrv(self.msrv, msrvs::MANUAL_BITS) { return; } diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index 8475e367b09fe..230ae029ed9d2 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -46,7 +46,7 @@ declare_clippy_lint! { declare_lint_pass!(ManualMap => [MANUAL_MAP]); impl<'tcx> LateLintPass<'tcx> for ManualMap { - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx 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), diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 09164690700ef..80845ace3f940 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; -use clippy_utils::{is_lint_allowed, meets_msrv, msrvs}; +use clippy_utils::{is_doc_hidden, is_lint_allowed, meets_msrv, msrvs}; use rustc_ast::ast::{self, VisibilityKind}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -61,7 +61,7 @@ declare_clippy_lint! { "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]" } -#[allow(clippy::module_name_repetitions)] +#[expect(clippy::module_name_repetitions)] pub struct ManualNonExhaustiveStruct { msrv: Option, } @@ -75,7 +75,7 @@ impl ManualNonExhaustiveStruct { impl_lint_pass!(ManualNonExhaustiveStruct => [MANUAL_NON_EXHAUSTIVE]); -#[allow(clippy::module_name_repetitions)] +#[expect(clippy::module_name_repetitions)] pub struct ManualNonExhaustiveEnum { msrv: Option, constructed_enum_variants: FxHashSet<(DefId, DefId)>, @@ -97,7 +97,7 @@ impl_lint_pass!(ManualNonExhaustiveEnum => [MANUAL_NON_EXHAUSTIVE]); impl EarlyLintPass for ManualNonExhaustiveStruct { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) { + if !meets_msrv(self.msrv, msrvs::NON_EXHAUSTIVE) { return; } @@ -149,7 +149,7 @@ impl EarlyLintPass for ManualNonExhaustiveStruct { impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) { + if !meets_msrv(self.msrv, msrvs::NON_EXHAUSTIVE) { return; } @@ -160,7 +160,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { let id = cx.tcx.hir().local_def_id(v.id); (matches!(v.data, hir::VariantData::Unit(_)) && v.ident.as_str().starts_with('_') - && cx.tcx.is_doc_hidden(id.to_def_id())) + && is_doc_hidden(cx.tcx.hir().attrs(v.id))) .then(|| (id, v.span)) }); if let Some((id, span)) = iter.next() diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index aacabf303a702..dfb3efc4e28b6 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -68,7 +68,7 @@ enum StripKind { impl<'tcx> LateLintPass<'tcx> for ManualStrip { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::STR_STRIP_PREFIX) { + if !meets_msrv(self.msrv, msrvs::STR_STRIP_PREFIX) { return; } diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index ceb66947d02c6..a13d191375bfa 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -144,7 +144,7 @@ impl MapClone { fn lint_explicit_closure(&self, cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool) { let mut applicability = Applicability::MachineApplicable; - let (message, sugg_method) = if is_copy && meets_msrv(self.msrv.as_ref(), &msrvs::ITERATOR_COPIED) { + let (message, sugg_method) = if is_copy && meets_msrv(self.msrv, msrvs::ITERATOR_COPIED) { ("you are using an explicit closure for copying elements", "copied") } else { ("you are using an explicit closure for cloning elements", "cloned") diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index 9b7344fb8b0b2..a96a7fe55f3a3 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -16,7 +16,7 @@ use std::collections::hash_map::Entry; use super::MATCH_SAME_ARMS; -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 { let mut h = SpanlessHash::new(cx); @@ -225,9 +225,9 @@ fn iter_matching_struct_fields<'a>( Iter(left.iter(), right.iter()) } -#[allow(clippy::similar_names)] +#[expect(clippy::similar_names)] impl<'a> NormalizedPat<'a> { - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn from_pat(cx: &LateContext<'_>, arena: &'a DroplessArena, pat: &'a Pat<'_>) -> Self { match pat.kind { PatKind::Wild | PatKind::Binding(.., None) => Self::Wild, diff --git a/clippy_lints/src/matches/match_single_binding.rs b/clippy_lints/src/matches/match_single_binding.rs index 39fe54648fbc7..a59711d4cace5 100644 --- a/clippy_lints/src/matches/match_single_binding.rs +++ b/clippy_lints/src/matches/match_single_binding.rs @@ -1,15 +1,22 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{indent_of, snippet_block, snippet_with_applicability}; +use clippy_utils::macros::HirNode; +use clippy_utils::source::{indent_of, snippet, snippet_block, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::{get_parent_expr, is_refutable, peel_blocks}; use rustc_errors::Applicability; -use rustc_hir::{Arm, Expr, ExprKind, Local, Node, PatKind}; +use rustc_hir::{Arm, Expr, ExprKind, Node, PatKind}; use rustc_lint::LateContext; +use rustc_span::Span; use super::MATCH_SINGLE_BINDING; -#[allow(clippy::too_many_lines)] -pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) { +enum AssignmentExpr { + Assign { span: Span, match_span: Span }, + Local { span: Span, pat_span: Span }, +} + +#[expect(clippy::too_many_lines)] +pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'a>) { if expr.span.from_expansion() || arms.len() != 1 || is_refutable(cx, arms[0].pat) { return; } @@ -42,61 +49,59 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e let mut applicability = Applicability::MaybeIncorrect; match arms[0].pat.kind { PatKind::Binding(..) | PatKind::Tuple(_, _) | PatKind::Struct(..) => { - // If this match is in a local (`let`) stmt - let (target_span, sugg) = if let Some(parent_let_node) = opt_parent_let(cx, ex) { - ( - parent_let_node.span, + let (target_span, sugg) = match opt_parent_assign_span(cx, ex) { + Some(AssignmentExpr::Assign { span, match_span }) => { + let sugg = sugg_with_curlies( + cx, + (ex, expr), + (bind_names, matched_vars), + &*snippet_body, + &mut applicability, + Some(span), + ); + + span_lint_and_sugg( + cx, + MATCH_SINGLE_BINDING, + span.to(match_span), + "this assignment could be simplified", + "consider removing the `match` expression", + sugg, + applicability, + ); + + return; + }, + Some(AssignmentExpr::Local { span, pat_span }) => ( + span, format!( "let {} = {};\n{}let {} = {};", snippet_with_applicability(cx, bind_names, "..", &mut applicability), snippet_with_applicability(cx, matched_vars, "..", &mut applicability), " ".repeat(indent_of(cx, expr.span).unwrap_or(0)), - snippet_with_applicability(cx, parent_let_node.pat.span, "..", &mut applicability), + snippet_with_applicability(cx, pat_span, "..", &mut applicability), snippet_body ), - ) - } else { - // If we are in closure, we need curly braces around suggestion - let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0)); - let (mut cbrace_start, mut cbrace_end) = ("".to_string(), "".to_string()); - if let Some(parent_expr) = get_parent_expr(cx, expr) { - if let ExprKind::Closure(..) = parent_expr.kind { - cbrace_end = format!("\n{}}}", indent); - // Fix body indent due to the closure - indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); - cbrace_start = format!("{{\n{}", indent); - } - } - // If the parent is already an arm, and the body is another match statement, - // we need curly braces around suggestion - let parent_node_id = cx.tcx.hir().get_parent_node(expr.hir_id); - if let Node::Arm(arm) = &cx.tcx.hir().get(parent_node_id) { - if let ExprKind::Match(..) = arm.body.kind { - cbrace_end = format!("\n{}}}", indent); - // Fix body indent due to the match - indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); - cbrace_start = format!("{{\n{}", indent); - } - } - ( - expr.span, - format!( - "{}let {} = {};\n{}{}{}", - cbrace_start, - snippet_with_applicability(cx, bind_names, "..", &mut applicability), - snippet_with_applicability(cx, matched_vars, "..", &mut applicability), - indent, - snippet_body, - cbrace_end - ), - ) + ), + None => { + let sugg = sugg_with_curlies( + cx, + (ex, expr), + (bind_names, matched_vars), + &*snippet_body, + &mut applicability, + None, + ); + (expr.span, sugg) + }, }; + span_lint_and_sugg( cx, MATCH_SINGLE_BINDING, target_span, "this match could be written as a `let` statement", - "consider using `let` statement", + "consider using a `let` statement", sugg, applicability, ); @@ -110,6 +115,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e indent, snippet_body ); + span_lint_and_sugg( cx, MATCH_SINGLE_BINDING, @@ -135,15 +141,76 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e } } -/// Returns true if the `ex` match expression is in a local (`let`) statement -fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<'a>> { +/// Returns true if the `ex` match expression is in a local (`let`) or assign expression +fn opt_parent_assign_span<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option { let map = &cx.tcx.hir(); - if_chain! { - if let Some(Node::Expr(parent_arm_expr)) = map.find(map.get_parent_node(ex.hir_id)); - if let Some(Node::Local(parent_let_expr)) = map.find(map.get_parent_node(parent_arm_expr.hir_id)); - then { - return Some(parent_let_expr); - } + + if let Some(Node::Expr(parent_arm_expr)) = map.find(map.get_parent_node(ex.hir_id)) { + return match map.find(map.get_parent_node(parent_arm_expr.hir_id)) { + Some(Node::Local(parent_let_expr)) => Some(AssignmentExpr::Local { + span: parent_let_expr.span, + pat_span: parent_let_expr.pat.span(), + }), + Some(Node::Expr(Expr { + kind: ExprKind::Assign(parent_assign_expr, match_expr, _), + .. + })) => Some(AssignmentExpr::Assign { + span: parent_assign_expr.span, + match_span: match_expr.span, + }), + _ => None, + }; } + None } + +fn sugg_with_curlies<'a>( + cx: &LateContext<'a>, + (ex, match_expr): (&Expr<'a>, &Expr<'a>), + (bind_names, matched_vars): (Span, Span), + snippet_body: &str, + applicability: &mut Applicability, + assignment: Option, +) -> String { + let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0)); + + let (mut cbrace_start, mut cbrace_end) = (String::new(), String::new()); + if let Some(parent_expr) = get_parent_expr(cx, match_expr) { + if let ExprKind::Closure(..) = parent_expr.kind { + cbrace_end = format!("\n{}}}", indent); + // Fix body indent due to the closure + indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); + cbrace_start = format!("{{\n{}", indent); + } + } + + // If the parent is already an arm, and the body is another match statement, + // we need curly braces around suggestion + let parent_node_id = cx.tcx.hir().get_parent_node(match_expr.hir_id); + if let Node::Arm(arm) = &cx.tcx.hir().get(parent_node_id) { + if let ExprKind::Match(..) = arm.body.kind { + cbrace_end = format!("\n{}}}", indent); + // Fix body indent due to the match + indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); + cbrace_start = format!("{{\n{}", indent); + } + } + + let assignment_str = assignment.map_or_else(String::new, |span| { + let mut s = snippet(cx, span, "..").to_string(); + s.push_str(" = "); + s + }); + + format!( + "{}let {} = {};\n{}{}{}{}", + cbrace_start, + snippet_with_applicability(cx, bind_names, "..", applicability), + snippet_with_applicability(cx, matched_vars, "..", applicability), + indent, + assignment_str, + snippet_body, + cbrace_end + ) +} diff --git a/clippy_lints/src/matches/match_wild_enum.rs b/clippy_lints/src/matches/match_wild_enum.rs index fc45ccee18528..6f8d766aef7c7 100644 --- a/clippy_lints/src/matches/match_wild_enum.rs +++ b/clippy_lints/src/matches/match_wild_enum.rs @@ -10,7 +10,7 @@ use rustc_span::sym; use super::{MATCH_WILDCARD_FOR_SINGLE_VARIANTS, WILDCARD_ENUM_MATCH_ARM}; -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { let ty = cx.typeck_results().expr_ty(ex).peel_refs(); let adt_def = match ty.kind() { @@ -56,7 +56,6 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { recurse_or_patterns(arm.pat, |pat| { let path = match &peel_hir_pat_refs(pat).0.kind { PatKind::Path(path) => { - #[allow(clippy::match_same_arms)] let id = match cx.qpath_res(path, pat.hir_id) { Res::Def( DefKind::Const | DefKind::ConstParam | DefKind::AnonConst | DefKind::InlineConst, diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index 401ecef460c35..3d8391bce2b28 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -658,7 +658,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } if !contains_cfg_arm(cx, expr, ex, arms) { if source == MatchSource::Normal { - if !(meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO) + if !(meets_msrv(self.msrv, msrvs::MATCHES_MACRO) && match_like_matches::check_match(cx, expr, ex, arms)) { match_same_arms::check(cx, arms); @@ -685,7 +685,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { match_wild_err_arm::check(cx, ex, arms); wild_in_or_pats::check(cx, arms); } else { - if meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO) { + if meets_msrv(self.msrv, msrvs::MATCHES_MACRO) { match_like_matches::check(cx, expr); } redundant_pattern_match::check(cx, expr); diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index 37b67647efe9e..1a8b9d15f370f 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -340,7 +340,7 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op } } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn find_good_method_for_match<'a>( cx: &LateContext<'_>, arms: &[Arm<'_>], diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index 054937e3e36b9..41073d40f3d79 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -254,7 +254,7 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace { then { check_replace_option_with_none(cx, src, dest, expr.span); check_replace_with_uninit(cx, src, dest, expr.span); - if meets_msrv(self.msrv.as_ref(), &msrvs::MEM_TAKE) { + if meets_msrv(self.msrv, msrvs::MEM_TAKE) { check_replace_with_default(cx, src, dest, expr.span); } } diff --git a/clippy_lints/src/methods/cloned_instead_of_copied.rs b/clippy_lints/src/methods/cloned_instead_of_copied.rs index 6d30bb5a278bb..e9aeab2d5b62e 100644 --- a/clippy_lints/src/methods/cloned_instead_of_copied.rs +++ b/clippy_lints/src/methods/cloned_instead_of_copied.rs @@ -10,16 +10,16 @@ use rustc_span::{sym, Span}; use super::CLONED_INSTEAD_OF_COPIED; -pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: Option<&RustcVersion>) { +pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: Option) { let recv_ty = cx.typeck_results().expr_ty_adjusted(recv); let inner_ty = match recv_ty.kind() { // `Option` -> `T` ty::Adt(adt, subst) - if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && meets_msrv(msrv, &msrvs::OPTION_COPIED) => + if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && meets_msrv(msrv, msrvs::OPTION_COPIED) => { subst.type_at(0) }, - _ if is_trait_method(cx, expr, sym::Iterator) && meets_msrv(msrv, &msrvs::ITERATOR_COPIED) => { + _ if is_trait_method(cx, expr, sym::Iterator) && meets_msrv(msrv, msrvs::ITERATOR_COPIED) => { match get_iterator_item_ty(cx, recv_ty) { // ::Item Some(ty) => ty, diff --git a/clippy_lints/src/methods/err_expect.rs b/clippy_lints/src/methods/err_expect.rs index be9d4ad94fb8e..570a1b87358dd 100644 --- a/clippy_lints/src/methods/err_expect.rs +++ b/clippy_lints/src/methods/err_expect.rs @@ -13,7 +13,7 @@ pub(super) fn check( cx: &LateContext<'_>, _expr: &rustc_hir::Expr<'_>, recv: &rustc_hir::Expr<'_>, - msrv: Option<&RustcVersion>, + msrv: Option, expect_span: Span, err_span: Span, ) { @@ -21,7 +21,7 @@ pub(super) fn check( if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); // Test the version to make sure the lint can be showed (expect_err has been // introduced in rust 1.17.0 : https://github.com/rust-lang/rust/pull/38982) - if meets_msrv(msrv, &msrvs::EXPECT_ERR); + if meets_msrv(msrv, msrvs::EXPECT_ERR); // Grabs the `Result` type let result_type = cx.typeck_results().expr_ty(recv); diff --git a/clippy_lints/src/methods/expect_used.rs b/clippy_lints/src/methods/expect_used.rs index 55be513c5bb1f..fbc3348f1855f 100644 --- a/clippy_lints/src/methods/expect_used.rs +++ b/clippy_lints/src/methods/expect_used.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_in_test_function; use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir as hir; use rustc_lint::LateContext; @@ -7,7 +8,7 @@ use rustc_span::sym; use super::EXPECT_USED; /// lint use of `expect()` for `Option`s and `Result`s -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, allow_expect_in_tests: bool) { let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) { @@ -18,6 +19,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr None }; + if allow_expect_in_tests && is_in_test_function(cx.tcx, expr.hir_id) { + return; + } + if let Some((lint, kind, none_value)) = mess { span_lint_and_help( cx, diff --git a/clippy_lints/src/methods/filter_map_next.rs b/clippy_lints/src/methods/filter_map_next.rs index f0d69a1f42e7b..38ec4d8e3ab8f 100644 --- a/clippy_lints/src/methods/filter_map_next.rs +++ b/clippy_lints/src/methods/filter_map_next.rs @@ -14,10 +14,10 @@ pub(super) fn check<'tcx>( expr: &'tcx hir::Expr<'_>, recv: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, - msrv: Option<&RustcVersion>, + msrv: Option, ) { if is_trait_method(cx, expr, sym::Iterator) { - if !meets_msrv(msrv, &msrvs::ITERATOR_FIND_MAP) { + if !meets_msrv(msrv, msrvs::ITERATOR_FIND_MAP) { return; } diff --git a/clippy_lints/src/methods/is_digit_ascii_radix.rs b/clippy_lints/src/methods/is_digit_ascii_radix.rs index ad333df2f2d5d..aa176dcc8b4af 100644 --- a/clippy_lints/src/methods/is_digit_ascii_radix.rs +++ b/clippy_lints/src/methods/is_digit_ascii_radix.rs @@ -15,9 +15,9 @@ pub(super) fn check<'tcx>( expr: &'tcx Expr<'_>, self_arg: &'tcx Expr<'_>, radix: &'tcx Expr<'_>, - msrv: Option<&RustcVersion>, + msrv: Option, ) { - if !meets_msrv(msrv, &msrvs::IS_ASCII_DIGIT) { + if !meets_msrv(msrv, msrvs::IS_ASCII_DIGIT) { return; } diff --git a/clippy_lints/src/methods/map_unwrap_or.rs b/clippy_lints/src/methods/map_unwrap_or.rs index 9ec84e76519ac..4a8e7ce4ddbbd 100644 --- a/clippy_lints/src/methods/map_unwrap_or.rs +++ b/clippy_lints/src/methods/map_unwrap_or.rs @@ -19,13 +19,13 @@ pub(super) fn check<'tcx>( recv: &'tcx hir::Expr<'_>, map_arg: &'tcx hir::Expr<'_>, unwrap_arg: &'tcx hir::Expr<'_>, - msrv: Option<&RustcVersion>, + msrv: Option, ) -> bool { // lint if the caller of `map()` is an `Option` let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option); let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); - if is_result && !meets_msrv(msrv, &msrvs::RESULT_MAP_OR_ELSE) { + if is_result && !meets_msrv(msrv, msrvs::RESULT_MAP_OR_ELSE) { return false; } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index f3be71f6b8bb8..35fc452ed7cf0 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1563,7 +1563,7 @@ declare_clippy_lint! { #[clippy::version = "1.39.0"] pub MANUAL_SATURATING_ARITHMETIC, style, - "`.chcked_add/sub(x).unwrap_or(MAX/MIN)`" + "`.checked_add/sub(x).unwrap_or(MAX/MIN)`" } declare_clippy_lint! { @@ -1776,8 +1776,6 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// use std::iter::FromIterator; - /// /// let five_fives = std::iter::repeat(5).take(5); /// /// let v = Vec::from_iter(five_fives); @@ -2200,14 +2198,23 @@ declare_clippy_lint! { pub struct Methods { avoid_breaking_exported_api: bool, msrv: Option, + allow_expect_in_tests: bool, + allow_unwrap_in_tests: bool, } impl Methods { #[must_use] - pub fn new(avoid_breaking_exported_api: bool, msrv: Option) -> Self { + pub fn new( + avoid_breaking_exported_api: bool, + msrv: Option, + allow_expect_in_tests: bool, + allow_unwrap_in_tests: bool, + ) -> Self { Self { avoid_breaking_exported_api, msrv, + allow_expect_in_tests, + allow_unwrap_in_tests, } } } @@ -2306,7 +2313,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { return; } - check_methods(cx, expr, self.msrv.as_ref()); + self.check_methods(cx, expr); match expr.kind { hir::ExprKind::Call(func, args) => { @@ -2322,7 +2329,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { single_char_add_str::check(cx, expr, args); into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, args); single_char_pattern::check(cx, expr, method_call.ident.name, args); - unnecessary_to_owned::check(cx, expr, method_call.ident.name, args, self.msrv.as_ref()); + unnecessary_to_owned::check(cx, expr, method_call.ident.name, args, self.msrv); }, hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => { let mut info = BinaryExprInfo { @@ -2505,196 +2512,201 @@ impl<'tcx> LateLintPass<'tcx> for Methods { extract_msrv_attr!(LateContext); } -#[allow(clippy::too_many_lines)] -fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Option<&RustcVersion>) { - if let Some((name, [recv, args @ ..], span)) = method_call(expr) { - match (name, args) { - ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => { - zst_offset::check(cx, expr, recv); - }, - ("and_then", [arg]) => { - let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, recv, arg); - let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, recv, arg); - if !biom_option_linted && !biom_result_linted { - unnecessary_lazy_eval::check(cx, expr, recv, arg, "and"); - } - }, - ("as_deref" | "as_deref_mut", []) => { - needless_option_as_deref::check(cx, expr, recv, name); - }, - ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv), - ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), - ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), - ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, msrv), - ("collect", []) => match method_call(recv) { - Some((name @ ("cloned" | "copied"), [recv2], _)) => { - iter_cloned_collect::check(cx, name, expr, recv2); +impl Methods { + #[allow(clippy::too_many_lines)] + fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let Some((name, [recv, args @ ..], span)) = method_call(expr) { + match (name, args) { + ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => { + zst_offset::check(cx, expr, recv); + }, + ("and_then", [arg]) => { + let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, recv, arg); + let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, recv, arg); + if !biom_option_linted && !biom_result_linted { + unnecessary_lazy_eval::check(cx, expr, recv, arg, "and"); + } + }, + ("as_deref" | "as_deref_mut", []) => { + needless_option_as_deref::check(cx, expr, recv, name); + }, + ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv), + ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), + ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), + ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, self.msrv), + ("collect", []) => match method_call(recv) { + Some((name @ ("cloned" | "copied"), [recv2], _)) => { + iter_cloned_collect::check(cx, name, expr, recv2); + }, + Some(("map", [m_recv, m_arg], _)) => { + map_collect_result_unit::check(cx, expr, m_recv, m_arg, recv); + }, + Some(("take", [take_self_arg, take_arg], _)) => { + if meets_msrv(self.msrv, msrvs::STR_REPEAT) { + manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); + } + }, + _ => {}, + }, + (name @ "count", args @ []) => match method_call(recv) { + Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args), + Some((name2 @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => { + iter_count::check(cx, expr, recv2, name2); + }, + Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg), + _ => {}, + }, + ("drain", [arg]) => { + iter_with_drain::check(cx, expr, recv, span, arg); + }, + ("expect", [_]) => match method_call(recv) { + Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv), + Some(("err", [recv], err_span)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span), + _ => expect_used::check(cx, expr, recv, self.allow_expect_in_tests), + }, + ("extend", [arg]) => { + string_extend_chars::check(cx, expr, recv, arg); + extend_with_drain::check(cx, expr, recv, arg); + }, + ("filter_map", [arg]) => { + unnecessary_filter_map::check(cx, expr, arg, name); + filter_map_identity::check(cx, expr, arg, span); }, - Some(("map", [m_recv, m_arg], _)) => { - map_collect_result_unit::check(cx, expr, m_recv, m_arg, recv); + ("find_map", [arg]) => { + unnecessary_filter_map::check(cx, expr, arg, name); }, - Some(("take", [take_self_arg, take_arg], _)) => { - if meets_msrv(msrv, &msrvs::STR_REPEAT) { - manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); + ("flat_map", [arg]) => { + flat_map_identity::check(cx, expr, arg, span); + flat_map_option::check(cx, expr, arg, span); + }, + (name @ "flatten", args @ []) => match method_call(recv) { + Some(("map", [recv, map_arg], map_span)) => map_flatten::check(cx, expr, recv, map_arg, map_span), + Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args), + _ => {}, + }, + ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span), + ("for_each", [_]) => { + if let Some(("inspect", [_, _], span2)) = method_call(recv) { + inspect_for_each::check(cx, expr, span2); } }, - _ => {}, - }, - (name @ "count", args @ []) => match method_call(recv) { - Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args), - Some((name2 @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => { - iter_count::check(cx, expr, recv2, name2); + ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"), + ("is_file", []) => filetype_is_file::check(cx, expr, recv), + ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv), + ("is_none", []) => check_is_some_is_none(cx, expr, recv, false), + ("is_some", []) => check_is_some_is_none(cx, expr, recv, true), + ("join", [join_arg]) => { + if let Some(("collect", _, span)) = method_call(recv) { + unnecessary_join::check(cx, expr, recv, join_arg, span); + } }, - Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg), - _ => {}, - }, - ("drain", [arg]) => { - iter_with_drain::check(cx, expr, recv, span, arg); - }, - ("expect", [_]) => match method_call(recv) { - Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv), - Some(("err", [recv], err_span)) => err_expect::check(cx, expr, recv, msrv, span, err_span), - _ => expect_used::check(cx, expr, recv), - }, - ("extend", [arg]) => { - string_extend_chars::check(cx, expr, recv, arg); - extend_with_drain::check(cx, expr, recv, arg); - }, - ("filter_map", [arg]) => { - unnecessary_filter_map::check(cx, expr, arg, name); - filter_map_identity::check(cx, expr, arg, span); - }, - ("find_map", [arg]) => { - unnecessary_filter_map::check(cx, expr, arg, name); - }, - ("flat_map", [arg]) => { - flat_map_identity::check(cx, expr, arg, span); - flat_map_option::check(cx, expr, arg, span); - }, - (name @ "flatten", args @ []) => match method_call(recv) { - Some(("map", [recv, map_arg], map_span)) => map_flatten::check(cx, expr, recv, map_arg, map_span), - Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args), - _ => {}, - }, - ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span), - ("for_each", [_]) => { - if let Some(("inspect", [_, _], span2)) = method_call(recv) { - inspect_for_each::check(cx, expr, span2); - } - }, - ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"), - ("is_file", []) => filetype_is_file::check(cx, expr, recv), - ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, msrv), - ("is_none", []) => check_is_some_is_none(cx, expr, recv, false), - ("is_some", []) => check_is_some_is_none(cx, expr, recv, true), - ("join", [join_arg]) => { - if let Some(("collect", _, span)) = method_call(recv) { - unnecessary_join::check(cx, expr, recv, join_arg, span); - } - }, - ("last", args @ []) | ("skip", args @ [_]) => { - if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) { - if let ("cloned", []) = (name2, args2) { - iter_overeager_cloned::check(cx, expr, recv2, name, args); + ("last", args @ []) | ("skip", args @ [_]) => { + if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) { + if let ("cloned", []) = (name2, args2) { + iter_overeager_cloned::check(cx, expr, recv2, name, args); + } } - } - }, - (name @ ("map" | "map_err"), [m_arg]) => { - if let Some((name, [recv2, args @ ..], span2)) = method_call(recv) { - match (name, args) { - ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, msrv), - ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, msrv), - ("filter", [f_arg]) => { - filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false); - }, - ("find", [f_arg]) => filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true), - _ => {}, + }, + (name @ ("map" | "map_err"), [m_arg]) => { + if let Some((name, [recv2, args @ ..], span2)) = method_call(recv) { + match (name, args) { + ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv), + ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, self.msrv), + ("filter", [f_arg]) => { + filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false); + }, + ("find", [f_arg]) => { + filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true); + }, + _ => {}, + } } - } - map_identity::check(cx, expr, recv, m_arg, name, span); - }, - ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map), - (name @ "next", args @ []) => { - if let Some((name2, [recv2, args2 @ ..], _)) = method_call(recv) { - match (name2, args2) { - ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv2, name, args), - ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg), - ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, msrv), - ("iter", []) => iter_next_slice::check(cx, expr, recv2), - ("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg), - ("skip_while", [_]) => skip_while_next::check(cx, expr), - _ => {}, + map_identity::check(cx, expr, recv, m_arg, name, span); + }, + ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map), + (name @ "next", args @ []) => { + if let Some((name2, [recv2, args2 @ ..], _)) = method_call(recv) { + match (name2, args2) { + ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv2, name, args), + ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg), + ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, self.msrv), + ("iter", []) => iter_next_slice::check(cx, expr, recv2), + ("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg), + ("skip_while", [_]) => skip_while_next::check(cx, expr), + _ => {}, + } } - } - }, - ("nth", args @ [n_arg]) => match method_call(recv) { - Some(("bytes", [recv2], _)) => bytes_nth::check(cx, expr, recv2, n_arg), - Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args), - Some(("iter", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false), - Some(("iter_mut", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true), - _ => iter_nth_zero::check(cx, expr, recv, n_arg), - }, - ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"), - ("or_else", [arg]) => { - if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, recv, arg) { - unnecessary_lazy_eval::check(cx, expr, recv, arg, "or"); - } - }, - ("splitn" | "rsplitn", [count_arg, pat_arg]) => { - if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) { - suspicious_splitn::check(cx, name, expr, recv, count); - str_splitn::check(cx, name, expr, recv, pat_arg, count, msrv); - } - }, - ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => { - if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) { - suspicious_splitn::check(cx, name, expr, recv, count); - } - }, - ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg), - ("take", args @ [_arg]) => { - if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) { - if let ("cloned", []) = (name2, args2) { - iter_overeager_cloned::check(cx, expr, recv2, name, args); + }, + ("nth", args @ [n_arg]) => match method_call(recv) { + Some(("bytes", [recv2], _)) => bytes_nth::check(cx, expr, recv2, n_arg), + Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args), + Some(("iter", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false), + Some(("iter_mut", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true), + _ => iter_nth_zero::check(cx, expr, recv, n_arg), + }, + ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"), + ("or_else", [arg]) => { + if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, recv, arg) { + unnecessary_lazy_eval::check(cx, expr, recv, arg, "or"); } - } - }, - ("take", []) => needless_option_take::check(cx, expr, recv), - ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => { - implicit_clone::check(cx, name, expr, recv); - }, - ("unwrap", []) => { - match method_call(recv) { - Some(("get", [recv, get_arg], _)) => { - get_unwrap::check(cx, expr, recv, get_arg, false); - }, - Some(("get_mut", [recv, get_arg], _)) => { - get_unwrap::check(cx, expr, recv, get_arg, true); + }, + ("splitn" | "rsplitn", [count_arg, pat_arg]) => { + if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) { + suspicious_splitn::check(cx, name, expr, recv, count); + str_splitn::check(cx, name, expr, recv, pat_arg, count, self.msrv); + } + }, + ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => { + if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) { + suspicious_splitn::check(cx, name, expr, recv, count); + } + }, + ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg), + ("take", args @ [_arg]) => { + if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) { + if let ("cloned", []) = (name2, args2) { + iter_overeager_cloned::check(cx, expr, recv2, name, args); + } + } + }, + ("take", []) => needless_option_take::check(cx, expr, recv), + ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => { + implicit_clone::check(cx, name, expr, recv); + }, + ("unwrap", []) => { + match method_call(recv) { + Some(("get", [recv, get_arg], _)) => { + get_unwrap::check(cx, expr, recv, get_arg, false); + }, + Some(("get_mut", [recv, get_arg], _)) => { + get_unwrap::check(cx, expr, recv, get_arg, true); + }, + Some(("or", [recv, or_arg], or_span)) => { + or_then_unwrap::check(cx, expr, recv, or_arg, or_span); + }, + _ => {}, + } + unwrap_used::check(cx, expr, recv, self.allow_unwrap_in_tests); + }, + ("unwrap_or", [u_arg]) => match method_call(recv) { + Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => { + manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]); }, - Some(("or", [recv, or_arg], or_span)) => { - or_then_unwrap::check(cx, expr, recv, or_arg, or_span); + Some(("map", [m_recv, m_arg], span)) => { + option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span); }, _ => {}, - } - unwrap_used::check(cx, expr, recv); - }, - ("unwrap_or", [u_arg]) => match method_call(recv) { - Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => { - manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]); }, - Some(("map", [m_recv, m_arg], span)) => { - option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span); + ("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, self.msrv) => {}, + _ => { + unwrap_or_else_default::check(cx, expr, recv, u_arg); + unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or"); + }, }, _ => {}, - }, - ("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) => {}, - _ => { - unwrap_or_else_default::check(cx, expr, recv, u_arg); - unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or"); - }, - }, - _ => {}, + } } } } @@ -2821,7 +2833,7 @@ const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ ShouldImplTraitCase::new("std::ops::Sub", "sub", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), ]; -#[derive(Clone, Copy, PartialEq, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] enum SelfKind { Value, Ref, diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index ba2d2914315f9..b50a173d8359b 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -19,9 +19,9 @@ pub(super) fn check<'tcx>( as_ref_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>, is_mut: bool, - msrv: Option<&RustcVersion>, + msrv: Option, ) { - if !meets_msrv(msrv, &msrvs::OPTION_AS_DEREF) { + if !meets_msrv(msrv, msrvs::OPTION_AS_DEREF) { return; } diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 52891eeed0696..90651a6ba0458 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -24,7 +24,7 @@ pub(super) fn check( self_arg: &Expr<'_>, pat_arg: &Expr<'_>, count: u128, - msrv: Option<&RustcVersion>, + msrv: Option, ) { if count < 2 || !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() { return; @@ -34,7 +34,7 @@ pub(super) fn check( IterUsageKind::Nth(n) => count > n + 1, IterUsageKind::NextTuple => count > 2, }; - let manual = count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE); + let manual = count == 2 && meets_msrv(msrv, msrvs::STR_SPLIT_ONCE); match parse_iter_usage(cx, expr.span.ctxt(), cx.tcx.hir().parent_iter(expr.hir_id)) { Some(usage) if needless(usage.kind) => lint_needless(cx, method_name, expr, self_arg, pat_arg), @@ -271,7 +271,7 @@ enum IterUsageKind { NextTuple, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] enum UnwrapKind { Unwrap, QuestionMark, diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 02b882e8b55e0..97c4feb3122a0 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -26,7 +26,7 @@ pub fn check<'tcx>( expr: &'tcx Expr<'tcx>, method_name: Symbol, args: &'tcx [Expr<'tcx>], - msrv: Option<&RustcVersion>, + msrv: Option, ) { if_chain! { if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); @@ -65,13 +65,12 @@ fn check_addr_of_expr( if let Some(parent) = get_parent_expr(cx, expr); if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) = parent.kind; let adjustments = cx.typeck_results().expr_adjustments(parent).iter().collect::>(); - if let Some(target_ty) = match adjustments[..] - { + if let // For matching uses of `Cow::from` [ Adjustment { kind: Adjust::Deref(None), - .. + target: referent_ty, }, Adjustment { kind: Adjust::Borrow(_), @@ -82,7 +81,7 @@ fn check_addr_of_expr( | [ Adjustment { kind: Adjust::Deref(None), - .. + target: referent_ty, }, Adjustment { kind: Adjust::Borrow(_), @@ -97,7 +96,7 @@ fn check_addr_of_expr( | [ Adjustment { kind: Adjust::Deref(None), - .. + target: referent_ty, }, Adjustment { kind: Adjust::Deref(Some(OverloadedDeref { .. })), @@ -107,17 +106,24 @@ fn check_addr_of_expr( kind: Adjust::Borrow(_), target: target_ty, }, - ] => Some(target_ty), - _ => None, - }; + ] = adjustments[..]; let receiver_ty = cx.typeck_results().expr_ty(receiver); - // Only flag cases where the receiver is copyable or the method is `Cow::into_owned`. This - // restriction is to ensure there is not overlap between `redundant_clone` and this lint. - if is_copy(cx, receiver_ty) || is_cow_into_owned(cx, method_name, method_def_id); + let (target_ty, n_target_refs) = peel_mid_ty_refs(*target_ty); + let (receiver_ty, n_receiver_refs) = peel_mid_ty_refs(receiver_ty); + // Only flag cases satisfying at least one of the following three conditions: + // * the referent and receiver types are distinct + // * the referent/receiver type is a copyable array + // * the method is `Cow::into_owned` + // This restriction is to ensure there is no overlap between `redundant_clone` and this + // lint. It also avoids the following false positive: + // https://github.com/rust-lang/rust-clippy/issues/8759 + // Arrays are a bit of a corner case. Non-copyable arrays are handled by + // `redundant_clone`, but copyable arrays are not. + if *referent_ty != receiver_ty + || (matches!(referent_ty.kind(), ty::Array(..)) && is_copy(cx, *referent_ty)) + || is_cow_into_owned(cx, method_name, method_def_id); if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); then { - let (target_ty, n_target_refs) = peel_mid_ty_refs(*target_ty); - let (receiver_ty, n_receiver_refs) = peel_mid_ty_refs(receiver_ty); if receiver_ty == target_ty && n_target_refs >= n_receiver_refs { span_lint_and_sugg( cx, @@ -192,7 +198,7 @@ fn check_into_iter_call_arg( expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>, - msrv: Option<&RustcVersion>, + msrv: Option, ) -> bool { if_chain! { if let Some(parent) = get_parent_expr(cx, expr); @@ -207,7 +213,11 @@ fn check_into_iter_call_arg( if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) { return true; } - let cloned_or_copied = if is_copy(cx, item_ty) && meets_msrv(msrv, &msrvs::ITERATOR_COPIED) { "copied" } else { "cloned" }; + let cloned_or_copied = if is_copy(cx, item_ty) && meets_msrv(msrv, msrvs::ITERATOR_COPIED) { + "copied" + } else { + "cloned" + }; // The next suggestion may be incorrect because the removal of the `to_owned`-like // function could cause the iterator to hold a reference to a resource that is used // mutably. See https://github.com/rust-lang/rust-clippy/issues/8148. diff --git a/clippy_lints/src/methods/unwrap_used.rs b/clippy_lints/src/methods/unwrap_used.rs index 44676d78c6076..5c761014927c2 100644 --- a/clippy_lints/src/methods/unwrap_used.rs +++ b/clippy_lints/src/methods/unwrap_used.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_in_test_function; use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir as hir; use rustc_lint::LateContext; @@ -7,7 +8,7 @@ use rustc_span::sym; use super::UNWRAP_USED; /// lint use of `unwrap()` for `Option`s and `Result`s -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, allow_unwrap_in_tests: bool) { let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) { @@ -18,6 +19,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr None }; + if allow_unwrap_in_tests && is_in_test_function(cx.tcx, expr.hir_id) { + return; + } + if let Some((lint, kind, none_value)) = mess { span_lint_and_help( cx, diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index ac82dd306a528..7fdc28c5a062d 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::source::{snippet, snippet_opt}; -use clippy_utils::ty::implements_trait; +use clippy_utils::ty::{implements_trait, is_copy}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -20,8 +20,8 @@ use rustc_span::symbol::sym; use clippy_utils::consts::{constant, Constant}; use clippy_utils::sugg::Sugg; use clippy_utils::{ - get_item_name, get_parent_expr, in_constant, is_diag_trait_item, is_integer_const, iter_input_pats, - last_path_segment, match_any_def_paths, path_def_id, paths, unsext, SpanlessEq, + get_item_name, get_parent_expr, in_constant, is_integer_const, iter_input_pats, last_path_segment, + match_any_def_paths, path_def_id, paths, unsext, SpanlessEq, }; declare_clippy_lint! { @@ -548,7 +548,7 @@ fn is_array(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { matches!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _)) } -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) { #[derive(Default)] struct EqImpl { @@ -569,33 +569,34 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: }) } - let (arg_ty, snip) = match expr.kind { - ExprKind::MethodCall(.., args, _) if args.len() == 1 => { - if_chain!( - if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if is_diag_trait_item(cx, expr_def_id, sym::ToString) - || is_diag_trait_item(cx, expr_def_id, sym::ToOwned); - then { - (cx.typeck_results().expr_ty(&args[0]), snippet(cx, args[0].span, "..")) - } else { - return; - } - ) + let typeck = cx.typeck_results(); + let (arg, arg_span) = match expr.kind { + ExprKind::MethodCall(.., [arg], _) + if typeck + .type_dependent_def_id(expr.hir_id) + .and_then(|id| cx.tcx.trait_of_item(id)) + .map_or(false, |id| { + matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ToString | sym::ToOwned)) + }) => + { + (arg, arg.span) }, - ExprKind::Call(path, [arg]) => { + ExprKind::Call(path, [arg]) if path_def_id(cx, path) .and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM])) - .is_some() - { - (cx.typeck_results().expr_ty(arg), snippet(cx, arg.span, "..")) - } else { - return; - } + .map_or(false, |idx| match idx { + 0 => true, + 1 => !is_copy(cx, typeck.expr_ty(expr)), + _ => false, + }) => + { + (arg, arg.span) }, _ => return, }; - let other_ty = cx.typeck_results().expr_ty(other); + let arg_ty = typeck.expr_ty(arg); + let other_ty = typeck.expr_ty(other); let without_deref = symmetric_partial_eq(cx, arg_ty, other_ty).unwrap_or_default(); let with_deref = arg_ty @@ -627,13 +628,14 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: return; } + let arg_snip = snippet(cx, arg_span, ".."); let expr_snip; let eq_impl; if with_deref.is_implemented() { - expr_snip = format!("*{}", snip); + expr_snip = format!("*{}", arg_snip); eq_impl = with_deref; } else { - expr_snip = snip.to_string(); + expr_snip = arg_snip.to_string(); eq_impl = without_deref; }; diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 06209bfe7b08a..16d65966c1009 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -93,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { span: Span, hir_id: HirId, ) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::CONST_IF_MATCH) { + if !meets_msrv(self.msrv, msrvs::CONST_IF_MATCH) { return; } @@ -146,7 +146,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { let mir = cx.tcx.optimized_mir(def_id); - if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv.as_ref()) { + if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv) { if cx.tcx.is_const_fn_raw(def_id.to_def_id()) { cx.tcx.sess.span_err(span, err.as_ref()); } diff --git a/clippy_lints/src/eval_order_dependence.rs b/clippy_lints/src/mixed_read_write_in_expression.rs similarity index 98% rename from clippy_lints/src/eval_order_dependence.rs rename to clippy_lints/src/mixed_read_write_in_expression.rs index 65599a0587d44..405fc23e8de0a 100644 --- a/clippy_lints/src/eval_order_dependence.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -40,8 +40,8 @@ declare_clippy_lint! { /// let a = tmp + x; /// ``` #[clippy::version = "pre 1.29.0"] - pub EVAL_ORDER_DEPENDENCE, - suspicious, + pub MIXED_READ_WRITE_IN_EXPRESSION, + restriction, "whether a variable read occurs before a write depends on sub-expression evaluation order" } @@ -73,7 +73,7 @@ declare_clippy_lint! { "whether an expression contains a diverging sub expression" } -declare_lint_pass!(EvalOrderDependence => [EVAL_ORDER_DEPENDENCE, DIVERGING_SUB_EXPRESSION]); +declare_lint_pass!(EvalOrderDependence => [MIXED_READ_WRITE_IN_EXPRESSION, DIVERGING_SUB_EXPRESSION]); impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { @@ -303,7 +303,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ReadVisitor<'a, 'tcx> { if !is_in_assignment_position(self.cx, expr) { span_lint_and_note( self.cx, - EVAL_ORDER_DEPENDENCE, + MIXED_READ_WRITE_IN_EXPRESSION, expr.span, &format!("unsequenced read of `{}`", self.cx.tcx.hir().name(self.var)), Some(self.write_expr.span), diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index cd1bc20237028..bb6d820b08cde 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -16,7 +16,7 @@ declare_clippy_lint! { /// ### Why is this bad? /// In release builds `debug_assert!` macros are optimized out by the /// compiler. - /// Therefore mutating something in a `debug_assert!` macro results in different behaviour + /// Therefore mutating something in a `debug_assert!` macro results in different behavior /// between a release and debug build. /// /// ### Example diff --git a/clippy_lints/src/needless_bitwise_bool.rs b/clippy_lints/src/needless_bitwise_bool.rs index 95395e2e136d9..623d22bc9bdfe 100644 --- a/clippy_lints/src/needless_bitwise_bool.rs +++ b/clippy_lints/src/needless_bitwise_bool.rs @@ -53,7 +53,7 @@ fn is_bitwise_operation(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { false } -fn suggesstion_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { +fn suggestion_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { if let ExprKind::Binary(ref op, left, right) = expr.kind { if let (Some(l_snippet), Some(r_snippet)) = (snippet_opt(cx, left.span), snippet_opt(cx, right.span)) { let op_snippet = match op.node { @@ -75,7 +75,7 @@ impl LateLintPass<'_> for NeedlessBitwiseBool { expr.span, "use of bitwise operator instead of lazy operator between booleans", |diag| { - if let Some(sugg) = suggesstion_snippet(cx, expr) { + if let Some(sugg) = suggestion_snippet(cx, expr) { diag.span_suggestion(expr.span, "try", sugg, Applicability::MachineApplicable); } }, diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 303ce7a5075aa..f423be4b67a61 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -70,7 +70,7 @@ macro_rules! need { } impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn check_fn( &mut self, cx: &LateContext<'tcx>, diff --git a/clippy_lints/src/neg_multiply.rs b/clippy_lints/src/neg_multiply.rs index 6ba9ba0753d49..707f3b2181ac9 100644 --- a/clippy_lints/src/neg_multiply.rs +++ b/clippy_lints/src/neg_multiply.rs @@ -34,7 +34,6 @@ declare_clippy_lint! { declare_lint_pass!(NegMultiply => [NEG_MULTIPLY]); -#[allow(clippy::match_same_arms)] impl<'tcx> LateLintPass<'tcx> for NegMultiply { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if let ExprKind::Binary(ref op, left, right) = e.kind { diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index 2bdccb425071b..093ec389335db 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -58,7 +58,6 @@ pub struct NewWithoutDefault { impl_lint_pass!(NewWithoutDefault => [NEW_WITHOUT_DEFAULT]); impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { - #[allow(clippy::too_many_lines)] fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { if let hir::ItemKind::Impl(hir::Impl { of_trait: None, diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index e3bc40c4b4914..7f6b535c7b16c 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -191,7 +191,7 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> { } } - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn check_ident(&mut self, ident: Ident) { let interned_name = ident.name.as_str(); if interned_name.chars().any(char::is_uppercase) { diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 9af3059a37f93..e3ded716341f6 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -52,7 +52,7 @@ declare_clippy_lint! { /// to a function that needs the memory address. For further details, refer to /// [this issue](https://github.com/rust-lang/rust-clippy/issues/5953) /// that explains a real case in which this false positive - /// led to an **undefined behaviour** introduced with unsafe code. + /// led to an **undefined behavior** introduced with unsafe code. /// /// ### Example /// @@ -124,7 +124,7 @@ impl<'tcx> PassByRefOrValue { // Cap the calculated bit width at 32-bits to reduce // portability problems between 32 and 64-bit targets let bit_width = cmp::min(bit_width, 32); - #[allow(clippy::integer_division)] + #[expect(clippy::integer_division)] let byte_width = bit_width / 8; // Use a limit of 2 times the register byte width byte_width * 2 diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index be319ee110d24..a4d265111f9ae 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -163,7 +163,6 @@ enum Level { Lower, } -#[allow(rustc::usage_of_ty_tykind)] fn find_first_mismatch<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>) -> Option<(Span, Mutability, Level)> { let mut result = None; pat.walk(|p| { diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index c35eeeac67a35..86460c1b27e39 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -514,7 +514,7 @@ fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Optio } } -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: &[PtrArg<'tcx>]) -> Vec { struct V<'cx, 'tcx> { cx: &'cx LateContext<'tcx>, diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index be7610f365c5b..26dc88a406e02 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -193,7 +193,7 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { check_range_zip_with_len(cx, path, args, expr.span); }, ExprKind::Binary(ref op, l, r) => { - if meets_msrv(self.msrv.as_ref(), &msrvs::RANGE_CONTAINS) { + if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) { check_possible_range_contains(cx, op.node, l, r, expr); } }, @@ -207,7 +207,13 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { extract_msrv_attr!(LateContext); } -fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'_>, r: &Expr<'_>, expr: &Expr<'_>) { +fn check_possible_range_contains( + cx: &LateContext<'_>, + op: BinOpKind, + left: &Expr<'_>, + right: &Expr<'_>, + expr: &Expr<'_>, +) { if in_constant(cx, expr.hir_id) { return; } @@ -219,21 +225,19 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<' _ => return, }; // value, name, order (higher/lower), inclusiveness - if let (Some((lval, lid, name_span, lval_span, lord, linc)), Some((rval, rid, _, rval_span, rord, rinc))) = - (check_range_bounds(cx, l), check_range_bounds(cx, r)) - { + if let (Some(l), Some(r)) = (check_range_bounds(cx, left), check_range_bounds(cx, right)) { // we only lint comparisons on the same name and with different // direction - if lid != rid || lord == rord { + if l.id != r.id || l.ord == r.ord { return; } - let ord = Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(l), &lval, &rval); - if combine_and && ord == Some(rord) { + let ord = Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(l.expr), &l.val, &r.val); + if combine_and && ord == Some(r.ord) { // order lower bound and upper bound - let (l_span, u_span, l_inc, u_inc) = if rord == Ordering::Less { - (lval_span, rval_span, linc, rinc) + let (l_span, u_span, l_inc, u_inc) = if r.ord == Ordering::Less { + (l.val_span, r.val_span, l.inc, r.inc) } else { - (rval_span, lval_span, rinc, linc) + (r.val_span, l.val_span, r.inc, l.inc) }; // we only lint inclusive lower bounds if !l_inc { @@ -245,7 +249,7 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<' ("Range", "..") }; let mut applicability = Applicability::MachineApplicable; - let name = snippet_with_applicability(cx, name_span, "_", &mut applicability); + let name = snippet_with_applicability(cx, l.name_span, "_", &mut applicability); let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability); let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability); let space = if lo.ends_with('.') { " " } else { "" }; @@ -258,13 +262,13 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<' format!("({}{}{}{}).contains(&{})", lo, space, range_op, hi, name), applicability, ); - } else if !combine_and && ord == Some(lord) { + } else if !combine_and && ord == Some(l.ord) { // `!_.contains(_)` // order lower bound and upper bound - let (l_span, u_span, l_inc, u_inc) = if lord == Ordering::Less { - (lval_span, rval_span, linc, rinc) + let (l_span, u_span, l_inc, u_inc) = if l.ord == Ordering::Less { + (l.val_span, r.val_span, l.inc, r.inc) } else { - (rval_span, lval_span, rinc, linc) + (r.val_span, l.val_span, r.inc, l.inc) }; if l_inc { return; @@ -275,7 +279,7 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<' ("RangeInclusive", "..=") }; let mut applicability = Applicability::MachineApplicable; - let name = snippet_with_applicability(cx, name_span, "_", &mut applicability); + let name = snippet_with_applicability(cx, l.name_span, "_", &mut applicability); let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability); let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability); let space = if lo.ends_with('.') { " " } else { "" }; @@ -292,7 +296,20 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<' } } -fn check_range_bounds(cx: &LateContext<'_>, ex: &Expr<'_>) -> Option<(Constant, HirId, Span, Span, Ordering, bool)> { +struct RangeBounds<'a> { + val: Constant, + expr: &'a Expr<'a>, + id: HirId, + name_span: Span, + val_span: Span, + ord: Ordering, + inc: bool, +} + +// Takes a binary expression such as x <= 2 as input +// Breaks apart into various pieces, such as the value of the number, +// hir id of the variable, and direction/inclusiveness of the operator +fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option> { if let ExprKind::Binary(ref op, l, r) = ex.kind { let (inclusive, ordering) = match op.node { BinOpKind::Gt => (false, Ordering::Greater), @@ -303,11 +320,27 @@ fn check_range_bounds(cx: &LateContext<'_>, ex: &Expr<'_>) -> Option<(Constant, }; if let Some(id) = path_to_local(l) { if let Some((c, _)) = constant(cx, cx.typeck_results(), r) { - return Some((c, id, l.span, r.span, ordering, inclusive)); + return Some(RangeBounds { + val: c, + expr: r, + id, + name_span: l.span, + val_span: r.span, + ord: ordering, + inc: inclusive, + }); } } else if let Some(id) = path_to_local(r) { if let Some((c, _)) = constant(cx, cx.typeck_results(), l) { - return Some((c, id, r.span, l.span, ordering.reverse(), inclusive)); + return Some(RangeBounds { + val: c, + expr: l, + id, + name_span: r.span, + val_span: l.span, + ord: ordering.reverse(), + inc: inclusive, + }); } } } diff --git a/clippy_lints/src/rc_clone_in_vec_init.rs b/clippy_lints/src/rc_clone_in_vec_init.rs new file mode 100644 index 0000000000000..110f58f3734df --- /dev/null +++ b/clippy_lints/src/rc_clone_in_vec_init.rs @@ -0,0 +1,141 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::higher::VecArgs; +use clippy_utils::last_path_segment; +use clippy_utils::macros::root_macro_call_first_node; +use clippy_utils::source::{indent_of, snippet}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, QPath, TyKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{sym, Span, Symbol}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for `Arc::new` or `Rc::new` in `vec![elem; len]` + /// + /// ### Why is this bad? + /// This will create `elem` once and clone it `len` times - doing so with `Arc` or `Rc` + /// is a bit misleading, as it will create references to the same pointer, rather + /// than different instances. + /// + /// ### Example + /// ```rust + /// let v = vec![std::sync::Arc::new("some data".to_string()); 100]; + /// // or + /// let v = vec![std::rc::Rc::new("some data".to_string()); 100]; + /// ``` + /// Use instead: + /// ```rust + /// + /// // Initialize each value separately: + /// let mut data = Vec::with_capacity(100); + /// for _ in 0..100 { + /// data.push(std::rc::Rc::new("some data".to_string())); + /// } + /// + /// // Or if you want clones of the same reference, + /// // Create the reference beforehand to clarify that + /// // it should be cloned for each value + /// let data = std::rc::Rc::new("some data".to_string()); + /// let v = vec![data; 100]; + /// ``` + #[clippy::version = "1.62.0"] + pub RC_CLONE_IN_VEC_INIT, + suspicious, + "initializing `Arc` or `Rc` in `vec![elem; len]`" +} +declare_lint_pass!(RcCloneInVecInit => [RC_CLONE_IN_VEC_INIT]); + +impl LateLintPass<'_> for RcCloneInVecInit { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return; }; + let Some(VecArgs::Repeat(elem, len)) = VecArgs::hir(cx, expr) else { return; }; + let Some(symbol) = new_reference_call(cx, elem) else { return; }; + + emit_lint(cx, symbol, macro_call.span, elem, len); + } +} + +fn elem_snippet(cx: &LateContext<'_>, elem: &Expr<'_>, symbol_name: &str) -> String { + let elem_snippet = snippet(cx, elem.span, "..").to_string(); + if elem_snippet.contains('\n') { + // This string must be found in `elem_snippet`, otherwise we won't be constructing + // the snippet in the first place. + let reference_creation = format!("{symbol_name}::new"); + let (code_until_reference_creation, _right) = elem_snippet.split_once(&reference_creation).unwrap(); + + return format!("{code_until_reference_creation}{reference_creation}(..)"); + } + + elem_snippet +} + +fn loop_init_suggestion(elem: &str, len: &str, indent: &str) -> String { + format!( + r#"{{ +{indent} let mut v = Vec::with_capacity({len}); +{indent} (0..{len}).for_each(|_| v.push({elem})); +{indent} v +{indent}}}"# + ) +} + +fn extract_suggestion(elem: &str, len: &str, indent: &str) -> String { + format!( + "{{ +{indent} let data = {elem}; +{indent} vec![data; {len}] +{indent}}}" + ) +} + +fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, elem: &Expr<'_>, len: &Expr<'_>) { + let symbol_name = symbol.as_str(); + + span_lint_and_then( + cx, + RC_CLONE_IN_VEC_INIT, + lint_span, + &format!("calling `{symbol_name}::new` in `vec![elem; len]`"), + |diag| { + let len_snippet = snippet(cx, len.span, ".."); + let elem_snippet = elem_snippet(cx, elem, symbol_name); + let indentation = " ".repeat(indent_of(cx, lint_span).unwrap_or(0)); + let loop_init_suggestion = loop_init_suggestion(&elem_snippet, len_snippet.as_ref(), &indentation); + let extract_suggestion = extract_suggestion(&elem_snippet, len_snippet.as_ref(), &indentation); + + diag.note(format!("each element will point to the same `{symbol_name}` instance")); + diag.span_suggestion( + lint_span, + format!("consider initializing each `{symbol_name}` element individually"), + loop_init_suggestion, + Applicability::Unspecified, + ); + diag.span_suggestion( + lint_span, + format!( + "or if this is intentional, consider extracting the `{symbol_name}` initialization to a variable" + ), + extract_suggestion, + Applicability::Unspecified, + ); + }, + ); +} + +/// Checks whether the given `expr` is a call to `Arc::new` or `Rc::new` +fn new_reference_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + if_chain! { + if let ExprKind::Call(func, _args) = expr.kind; + if let ExprKind::Path(ref func_path @ QPath::TypeRelative(ty, _)) = func.kind; + if let TyKind::Path(ref ty_path) = ty.kind; + if let Some(def_id) = cx.qpath_res(ty_path, ty.hir_id).opt_def_id(); + if last_path_segment(func_path).ident.name == sym::new; + + then { + return cx.tcx.get_diagnostic_name(def_id).filter(|symbol| symbol == &sym::Arc || symbol == &sym::Rc); + } + } + + None +} diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 37aac8b2a4978..0004b8afdd375 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -19,7 +19,6 @@ use rustc_mir_dataflow::{Analysis, AnalysisDomain, CallReturnPlaces, GenKill, Ge use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::{BytePos, Span}; use rustc_span::sym; -use std::convert::TryFrom; use std::ops::ControlFlow; macro_rules! unwrap_or_continue { @@ -71,7 +70,7 @@ declare_clippy_lint! { declare_lint_pass!(RedundantClone => [REDUNDANT_CLONE]); impl<'tcx> LateLintPass<'tcx> for RedundantClone { - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn check_fn( &mut self, cx: &LateContext<'tcx>, @@ -633,7 +632,7 @@ impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> { } /// Collect possible borrowed for every `&mut` local. -/// For exampel, `_1 = &mut _2` generate _1: {_2,...} +/// For example, `_1 = &mut _2` generate _1: {_2,...} /// Known Problems: not sure all borrowed are tracked struct PossibleOriginVisitor<'a, 'tcx> { possible_origin: TransitiveRelation, diff --git a/clippy_lints/src/redundant_field_names.rs b/clippy_lints/src/redundant_field_names.rs index 40a62fd6d2013..40b03068f6c77 100644 --- a/clippy_lints/src/redundant_field_names.rs +++ b/clippy_lints/src/redundant_field_names.rs @@ -51,7 +51,7 @@ impl_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]); impl EarlyLintPass for RedundantFieldNames { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::FIELD_INIT_SHORTHAND) { + if !meets_msrv(self.msrv, msrvs::FIELD_INIT_SHORTHAND) { return; } diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index ea5064217abe5..2d26c49252fa5 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -99,7 +99,7 @@ impl RedundantStaticLifetimes { impl EarlyLintPass for RedundantStaticLifetimes { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::STATIC_IN_CONST) { + if !meets_msrv(self.msrv, msrvs::STATIC_IN_CONST) { return; } diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index 811a7bb9c153a..f789cec6d6acf 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -54,13 +54,12 @@ impl EarlyLintPass for DerefAddrOf { then { let mut applicability = Applicability::MachineApplicable; let sugg = if e.span.from_expansion() { - #[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| { snippet_opt(cx, span).and_then(|snip| { - #[allow(clippy::cast_possible_truncation)] + #[expect(clippy::cast_possible_truncation)] snip.find(|c: char| !c.is_whitespace()).map(|pos| { span.lo() + BytePos(pos as u32) }) @@ -68,7 +67,7 @@ impl EarlyLintPass for DerefAddrOf { }; let mut generate_snippet = |pattern: &str| { - #[allow(clippy::cast_possible_truncation)] + #[expect(clippy::cast_possible_truncation)] macro_source.rfind(pattern).map(|pattern_pos| { let rpos = pattern_pos + pattern.len(); let span_after_ref = e.span.with_lo(BytePos(e.span.lo().0 + rpos as u32)); diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index a92097e1d24ca..67129299e2f92 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -7,7 +7,6 @@ use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::{BytePos, Span}; -use std::convert::TryFrom; declare_clippy_lint! { /// ### What it does @@ -79,7 +78,6 @@ impl<'tcx> LateLintPass<'tcx> for Regex { } } -#[allow(clippy::cast_possible_truncation)] // truncation very unlikely here #[must_use] fn str_span(base: Span, c: regex_syntax::ast::Span, offset: u8) -> Span { let offset = u32::from(offset); diff --git a/clippy_lints/src/renamed_lints.rs b/clippy_lints/src/renamed_lints.rs index bfc03116fe2d9..ba03ef9372118 100644 --- a/clippy_lints/src/renamed_lints.rs +++ b/clippy_lints/src/renamed_lints.rs @@ -9,6 +9,7 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"), ("clippy::disallowed_method", "clippy::disallowed_methods"), ("clippy::disallowed_type", "clippy::disallowed_types"), + ("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"), ("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"), ("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"), ("clippy::identity_conversion", "clippy::useless_conversion"), diff --git a/clippy_lints/src/significant_drop_in_scrutinee.rs b/clippy_lints/src/significant_drop_in_scrutinee.rs index f300acf0fb2ba..424b361a905ce 100644 --- a/clippy_lints/src/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/significant_drop_in_scrutinee.rs @@ -29,7 +29,7 @@ declare_clippy_lint! { /// the match block and thus will not unlock. /// /// ### Example - /// ```rust + /// ```rust.ignore /// # use std::sync::Mutex; /// /// # struct State {} @@ -83,7 +83,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.60.0"] pub SIGNIFICANT_DROP_IN_SCRUTINEE, - nursery, + suspicious, "warns when a temporary of a type with a drop with a significant side-effect might have a surprising lifetime" } @@ -99,7 +99,7 @@ impl<'tcx> LateLintPass<'tcx> for SignificantDropInScrutinee { found.found_span, "temporary with significant drop in match scrutinee", |diag| set_diagnostic(diag, cx, expr, found), - ) + ); } } } @@ -148,8 +148,8 @@ fn set_diagnostic<'tcx>(diag: &mut Diagnostic, cx: &LateContext<'tcx>, expr: &'t ); } -/// If the expression is an ExprKind::Match, check if the scrutinee has a significant drop that may -/// have a surprising lifetime. +/// If the expression is an `ExprKind::Match`, check if the scrutinee has a significant drop that +/// may have a surprising lifetime. fn has_significant_drop_in_scrutinee<'tcx, 'a>( cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>, @@ -171,6 +171,7 @@ struct SigDropHelper<'a, 'tcx> { special_handling_for_binary_op: bool, } +#[expect(clippy::enum_variant_names)] #[derive(Debug, PartialEq, Eq, Clone, Copy)] enum LintSuggestion { MoveOnly, @@ -213,7 +214,7 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> { } /// This will try to set the current suggestion (so it can be moved into the suggestions vec - /// later). If allow_move_and_clone is false, the suggestion *won't* be set -- this gives us + /// later). If `allow_move_and_clone` is false, the suggestion *won't* be set -- this gives us /// an opportunity to look for another type in the chain that will be trivially copyable. /// However, if we are at the the end of the chain, we want to accept whatever is there. (The /// suggestion won't actually be output, but the diagnostic message will be output, so the user @@ -313,10 +314,10 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> { } false }, - rustc_middle::ty::Array(ty, _) => self.has_sig_drop_attr(cx, *ty), - rustc_middle::ty::RawPtr(TypeAndMut { ty, .. }) => self.has_sig_drop_attr(cx, *ty), - rustc_middle::ty::Ref(_, ty, _) => self.has_sig_drop_attr(cx, *ty), - rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(cx, *ty), + rustc_middle::ty::Array(ty, _) + | rustc_middle::ty::RawPtr(TypeAndMut { ty, .. }) + | rustc_middle::ty::Ref(_, ty, _) + | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(cx, *ty), _ => false, } } @@ -332,15 +333,12 @@ impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> { match ex.kind { ExprKind::MethodCall(_, [ref expr, ..], _) => { - self.visit_expr(expr) + self.visit_expr(expr); } ExprKind::Binary(_, left, right) => { self.visit_exprs_for_binary_ops(left, right, false, ex.span); } - ExprKind::Assign(left, right, _) => { - self.visit_exprs_for_binary_ops(left, right, true, ex.span); - } - ExprKind::AssignOp(_, left, right) => { + ExprKind::Assign(left, right, _) | ExprKind::AssignOp(_, left, right) => { self.visit_exprs_for_binary_ops(left, right, true, ex.span); } ExprKind::Tup(exprs) => { diff --git a/clippy_lints/src/tabs_in_doc_comments.rs b/clippy_lints/src/tabs_in_doc_comments.rs index 15543b6a26277..e223aea297fc4 100644 --- a/clippy_lints/src/tabs_in_doc_comments.rs +++ b/clippy_lints/src/tabs_in_doc_comments.rs @@ -4,7 +4,6 @@ use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::{BytePos, Span}; -use std::convert::TryFrom; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 342f23f030cd0..d2a040beb0cf7 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -27,7 +27,7 @@ declare_clippy_lint! { /// architecture. /// /// ### Why is this bad? - /// It's basically guaranteed to be undefined behaviour. + /// It's basically guaranteed to be undefined behavior. /// /// ### Known problems /// When accessing C, users might want to store pointer @@ -40,7 +40,7 @@ declare_clippy_lint! { #[clippy::version = "pre 1.29.0"] pub WRONG_TRANSMUTE, correctness, - "transmutes that are confusing at best, undefined behaviour at worst and always useless" + "transmutes that are confusing at best, undefined behavior at worst and always useless" } // FIXME: Move this to `complexity` again, after #5343 is fixed diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs index 10d2ae2eb1dbb..a1312fcda0b71 100644 --- a/clippy_lints/src/types/redundant_allocation.rs +++ b/clippy_lints/src/types/redundant_allocation.rs @@ -5,6 +5,7 @@ use rustc_errors::Applicability; use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind}; use rustc_lint::LateContext; use rustc_span::symbol::sym; +use rustc_typeck::hir_ty_to_ty; use super::{utils, REDUNDANT_ALLOCATION}; @@ -54,8 +55,10 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ }; let inner_span = match qpath_generic_tys(inner_qpath).next() { Some(ty) => { - // Box> is smaller than Box because of wide pointers - if matches!(ty.kind, TyKind::TraitObject(..)) { + // Reallocation of a fat pointer causes it to become thin. `hir_ty_to_ty` is safe to use + // here because `mod.rs` guarantees this lint is only run on types outside of bodies and + // is not run on locals. + if !hir_ty_to_ty(cx.tcx, ty).is_sized(cx.tcx.at(ty.span), cx.param_env) { return false; } ty.span diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index 465d8a914fb29..5a8677f90be41 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -1,17 +1,18 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_lint_allowed; use clippy_utils::source::walk_span_to_context; +use clippy_utils::{get_parent_node, is_lint_allowed}; use rustc_data_structures::sync::Lrc; -use rustc_hir::{Block, BlockCheckMode, UnsafeSource}; +use rustc_hir as hir; +use rustc_hir::{Block, BlockCheckMode, ItemKind, Node, UnsafeSource}; use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{BytePos, Pos, SyntaxContext}; +use rustc_span::{BytePos, Pos, Span, SyntaxContext}; declare_clippy_lint! { /// ### What it does - /// Checks for `unsafe` blocks without a `// SAFETY: ` comment + /// Checks for `unsafe` blocks and impls without a `// SAFETY: ` comment /// explaining why the unsafe operations performed inside /// the block are safe. /// @@ -34,7 +35,7 @@ declare_clippy_lint! { /// ``` /// /// ### Why is this bad? - /// Undocumented unsafe blocks can make it difficult to + /// Undocumented unsafe blocks and impls can make it difficult to /// read and maintain code, as well as uncover unsoundness /// and bugs. /// @@ -66,7 +67,7 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks { if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) && !in_external_macro(cx.tcx.sess, block.span) && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id) - && !is_unsafe_from_proc_macro(cx, block) + && !is_unsafe_from_proc_macro(cx, block.span) && !block_has_safety_comment(cx, block) { let source_map = cx.tcx.sess.source_map(); @@ -86,11 +87,37 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks { ); } } + + fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { + if let hir::ItemKind::Impl(imple) = item.kind + && imple.unsafety == hir::Unsafety::Unsafe + && !in_external_macro(cx.tcx.sess, item.span) + && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, item.hir_id()) + && !is_unsafe_from_proc_macro(cx, item.span) + && !item_has_safety_comment(cx, item) + { + let source_map = cx.tcx.sess.source_map(); + let span = if source_map.is_multiline(item.span) { + source_map.span_until_char(item.span, '\n') + } else { + item.span + }; + + span_lint_and_help( + cx, + UNDOCUMENTED_UNSAFE_BLOCKS, + span, + "unsafe impl missing a safety comment", + None, + "consider adding a safety comment on the preceding line", + ); + } + } } -fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, block: &Block<'_>) -> bool { +fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, span: Span) -> bool { let source_map = cx.sess().source_map(); - let file_pos = source_map.lookup_byte_offset(block.span.lo()); + let file_pos = source_map.lookup_byte_offset(span.lo()); file_pos .sf .src @@ -100,7 +127,7 @@ fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, block: &Block<'_>) -> bool { } /// Checks if the lines immediately preceding the block contain a safety comment. -fn block_has_safety_comment(cx: &LateContext<'_>, block: &Block<'_>) -> bool { +fn block_has_safety_comment(cx: &LateContext<'_>, block: &hir::Block<'_>) -> bool { // This intentionally ignores text before the start of a function so something like: // ``` // // SAFETY: reason @@ -109,13 +136,115 @@ fn block_has_safety_comment(cx: &LateContext<'_>, block: &Block<'_>) -> bool { // won't work. This is to avoid dealing with where such a comment should be place relative to // attributes and doc comments. + span_from_macro_expansion_has_safety_comment(cx, block.span) || span_in_body_has_safety_comment(cx, block.span) +} + +/// Checks if the lines immediately preceding the item contain a safety comment. +#[allow(clippy::collapsible_match)] +fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> bool { + if span_from_macro_expansion_has_safety_comment(cx, item.span) { + return true; + } + + if item.span.ctxt() == SyntaxContext::root() { + if let Some(parent_node) = get_parent_node(cx.tcx, item.hir_id()) { + let comment_start = match parent_node { + Node::Crate(parent_mod) => { + comment_start_before_impl_in_mod(cx, parent_mod, parent_mod.spans.inner_span, item) + }, + Node::Item(parent_item) => { + if let ItemKind::Mod(parent_mod) = &parent_item.kind { + comment_start_before_impl_in_mod(cx, parent_mod, parent_item.span, item) + } else { + // Doesn't support impls in this position. Pretend a comment was found. + return true; + } + }, + Node::Stmt(stmt) => { + if let Some(stmt_parent) = get_parent_node(cx.tcx, stmt.hir_id) { + match stmt_parent { + Node::Block(block) => walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo), + _ => { + // Doesn't support impls in this position. Pretend a comment was found. + return true; + }, + } + } else { + // Problem getting the parent node. Pretend a comment was found. + return true; + } + }, + _ => { + // Doesn't support impls in this position. Pretend a comment was found. + return true; + }, + }; + + let source_map = cx.sess().source_map(); + if let Some(comment_start) = comment_start + && let Ok(unsafe_line) = source_map.lookup_line(item.span.lo()) + && let Ok(comment_start_line) = source_map.lookup_line(comment_start) + && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf) + && let Some(src) = unsafe_line.sf.src.as_deref() + { + comment_start_line.line < unsafe_line.line && text_has_safety_comment( + src, + &unsafe_line.sf.lines[comment_start_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos.to_usize(), + ) + } else { + // Problem getting source text. Pretend a comment was found. + true + } + } else { + // No parent node. Pretend a comment was found. + true + } + } else { + false + } +} + +fn comment_start_before_impl_in_mod( + cx: &LateContext<'_>, + parent_mod: &hir::Mod<'_>, + parent_mod_span: Span, + imple: &hir::Item<'_>, +) -> Option { + parent_mod.item_ids.iter().enumerate().find_map(|(idx, item_id)| { + if *item_id == imple.item_id() { + if idx == 0 { + // mod A { /* comment */ unsafe impl T {} ... } + // ^------------------------------------------^ returns the start of this span + // ^---------------------^ finally checks comments in this range + if let Some(sp) = walk_span_to_context(parent_mod_span, SyntaxContext::root()) { + return Some(sp.lo()); + } + } else { + // some_item /* comment */ unsafe impl T {} + // ^-------^ returns the end of this span + // ^---------------^ finally checks comments in this range + let prev_item = cx.tcx.hir().item(parent_mod.item_ids[idx - 1]); + if let Some(sp) = walk_span_to_context(prev_item.span, SyntaxContext::root()) { + return Some(sp.hi()); + } + } + } + None + }) +} + +fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { let source_map = cx.sess().source_map(); - let ctxt = block.span.ctxt(); - if ctxt != SyntaxContext::root() { - // From a macro expansion. Get the text from the start of the macro declaration to start of the unsafe block. + let ctxt = span.ctxt(); + if ctxt == SyntaxContext::root() { + false + } else { + // From a macro expansion. Get the text from the start of the macro declaration to start of the + // unsafe block. // macro_rules! foo { () => { stuff }; (x) => { unsafe { stuff } }; } // ^--------------------------------------------^ - if let Ok(unsafe_line) = source_map.lookup_line(block.span.lo()) + if let Ok(unsafe_line) = source_map.lookup_line(span.lo()) && let Ok(macro_line) = source_map.lookup_line(ctxt.outer_expn_data().def_site.lo()) && Lrc::ptr_eq(&unsafe_line.sf, ¯o_line.sf) && let Some(src) = unsafe_line.sf.src.as_deref() @@ -129,24 +258,35 @@ fn block_has_safety_comment(cx: &LateContext<'_>, block: &Block<'_>) -> bool { // Problem getting source text. Pretend a comment was found. true } - } else if let Ok(unsafe_line) = source_map.lookup_line(block.span.lo()) + } +} + +fn span_in_body_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { + let source_map = cx.sess().source_map(); + let ctxt = span.ctxt(); + if ctxt == SyntaxContext::root() && let Some(body) = cx.enclosing_body - && let Some(body_span) = walk_span_to_context(cx.tcx.hir().body(body).value.span, SyntaxContext::root()) - && let Ok(body_line) = source_map.lookup_line(body_span.lo()) - && Lrc::ptr_eq(&unsafe_line.sf, &body_line.sf) - && let Some(src) = unsafe_line.sf.src.as_deref() { - // Get the text from the start of function body to the unsafe block. - // fn foo() { some_stuff; unsafe { stuff }; other_stuff; } - // ^-------------^ - body_line.line < unsafe_line.line && text_has_safety_comment( - src, - &unsafe_line.sf.lines[body_line.line + 1..=unsafe_line.line], - unsafe_line.sf.start_pos.to_usize(), - ) + if let Ok(unsafe_line) = source_map.lookup_line(span.lo()) + && let Some(body_span) = walk_span_to_context(cx.tcx.hir().body(body).value.span, SyntaxContext::root()) + && let Ok(body_line) = source_map.lookup_line(body_span.lo()) + && Lrc::ptr_eq(&unsafe_line.sf, &body_line.sf) + && let Some(src) = unsafe_line.sf.src.as_deref() + { + // Get the text from the start of function body to the unsafe block. + // fn foo() { some_stuff; unsafe { stuff }; other_stuff; } + // ^-------------^ + body_line.line < unsafe_line.line && text_has_safety_comment( + src, + &unsafe_line.sf.lines[body_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos.to_usize(), + ) + } else { + // Problem getting source text. Pretend a comment was found. + true + } } else { - // Problem getting source text. Pretend a comment was found. - true + false } } diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index 6d909c34690d4..9f4c5555f11b7 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -98,7 +98,7 @@ fn handle_uninit_vec_pair<'tcx>( // Check T of Vec if !is_uninit_value_valid_for_ty(cx, substs.type_at(0)) { // FIXME: #7698, false positive of the internal lints - #[allow(clippy::collapsible_span_lint_calls)] + #[expect(clippy::collapsible_span_lint_calls)] span_lint_and_then( cx, UNINIT_VEC, diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index f3f1f53aac565..d86002c926efe 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -3,6 +3,7 @@ use clippy_utils::source::snippet_with_macro_callsite; use clippy_utils::visitors::for_each_value_source; use core::ops::ControlFlow; use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind, PatKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; @@ -71,14 +72,18 @@ fn needs_inferred_result_ty(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { .. }, _, - ) => cx.qpath_res(path, *hir_id).opt_def_id(), - ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(e.hir_id), + ) => match cx.qpath_res(path, *hir_id) { + Res::Def(DefKind::AssocFn | DefKind::Fn, id) => id, + _ => return false, + }, + ExprKind::MethodCall(..) => match cx.typeck_results().type_dependent_def_id(e.hir_id) { + Some(id) => id, + None => return false, + }, _ => return false, }; - if let Some(id) = id - && let sig = cx.tcx.fn_sig(id).skip_binder() - && let ty::Param(output_ty) = *sig.output().kind() - { + let sig = cx.tcx.fn_sig(id).skip_binder(); + if let ty::Param(output_ty) = *sig.output().kind() { sig.inputs().iter().all(|&ty| !ty_contains_param(ty, output_ty.index)) } else { false diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index ae431aac83b82..04e2f301bfd88 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -61,13 +61,13 @@ impl_lint_pass!(UnnestedOrPatterns => [UNNESTED_OR_PATTERNS]); impl EarlyLintPass for UnnestedOrPatterns { fn check_arm(&mut self, cx: &EarlyContext<'_>, a: &ast::Arm) { - if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) { + if meets_msrv(self.msrv, msrvs::OR_PATTERNS) { lint_unnested_or_patterns(cx, &a.pat); } } fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { - if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) { + if meets_msrv(self.msrv, msrvs::OR_PATTERNS) { if let ast::ExprKind::Let(pat, _, _) = &e.kind { lint_unnested_or_patterns(cx, pat); } @@ -75,13 +75,13 @@ impl EarlyLintPass for UnnestedOrPatterns { } fn check_param(&mut self, cx: &EarlyContext<'_>, p: &ast::Param) { - if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) { + if meets_msrv(self.msrv, msrvs::OR_PATTERNS) { lint_unnested_or_patterns(cx, &p.pat); } } fn check_local(&mut self, cx: &EarlyContext<'_>, l: &ast::Local) { - if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) { + if meets_msrv(self.msrv, msrvs::OR_PATTERNS) { lint_unnested_or_patterns(cx, &l.pat); } } diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index bfd17a6874994..52585e59566c8 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -130,7 +130,7 @@ fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { 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)] + #[expect(clippy::cast_possible_truncation)] ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), Applicability::MachineApplicable, ) diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 138f8bccb3f57..66f7748e9e089 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -198,7 +198,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) { if_chain! { if !hir_ty.span.from_expansion(); - if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS); + if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS); if let Some(&StackItem::Check { impl_id, in_body, @@ -225,7 +225,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { if !expr.span.from_expansion(); - if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS); + if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS); if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last(); if cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id); then {} else { return; } @@ -256,7 +256,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) { if_chain! { if !pat.span.from_expansion(); - if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS); + if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS); if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last(); if let PatKind::Path(QPath::Resolved(_, path)) = pat.kind; if !matches!(path.res, Res::SelfTy { .. } | Res::Def(DefKind::TyParam, _)); diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index abd8a3623703b..4a3b5383c892b 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -41,7 +41,7 @@ pub struct UselessConversion { impl_lint_pass!(UselessConversion => [USELESS_CONVERSION]); -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] impl<'tcx> LateLintPass<'tcx> for UselessConversion { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if e.span.from_expansion() { diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 74b0168a1794b..cd4d16fe95f75 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -6,7 +6,8 @@ use serde::de::{Deserializer, IgnoredAny, IntoDeserializer, MapAccess, Visitor}; use serde::Deserialize; use std::error::Error; use std::path::{Path, PathBuf}; -use std::{env, fmt, fs, io}; +use std::str::FromStr; +use std::{cmp, env, fmt, fs, io, iter}; /// Holds information used by `MISSING_ENFORCED_IMPORT_RENAMES` lint. #[derive(Clone, Debug, Deserialize)] @@ -43,18 +44,33 @@ pub enum DisallowedType { #[derive(Default)] pub struct TryConf { pub conf: Conf, - pub errors: Vec, + pub errors: Vec>, } impl TryConf { - fn from_error(error: impl Error) -> Self { + fn from_error(error: impl Error + 'static) -> Self { Self { conf: Conf::default(), - errors: vec![error.to_string()], + errors: vec![Box::new(error)], } } } +#[derive(Debug)] +struct ConfError(String); + +impl fmt::Display for ConfError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ::fmt(&self.0, f) + } +} + +impl Error for ConfError {} + +fn conf_error(s: String) -> Box { + Box::new(ConfError(s)) +} + macro_rules! define_Conf { ($( $(#[doc = $doc:literal])+ @@ -103,11 +119,11 @@ macro_rules! define_Conf { while let Some(name) = map.next_key::<&str>()? { match Field::deserialize(name.into_deserializer())? { $(Field::$name => { - $(errors.push(format!("deprecated field `{}`. {}", name, $dep));)? + $(errors.push(conf_error(format!("deprecated field `{}`. {}", name, $dep)));)? match map.next_value() { - Err(e) => errors.push(e.to_string()), + Err(e) => errors.push(conf_error(e.to_string())), Ok(value) => match $name { - Some(_) => errors.push(format!("duplicate field `{}`", name)), + Some(_) => errors.push(conf_error(format!("duplicate field `{}`", name))), None => $name = Some(value), } } @@ -316,6 +332,14 @@ define_Conf! { /// /// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes (max_include_file_size: u64 = 1_000_000), + /// Lint: EXPECT_USED. + /// + /// Whether `expect` should be allowed in test functions + (allow_expect_in_tests: bool = false), + /// Lint: UNWRAP_USED. + /// + /// Whether `unwrap` should be allowed in test functions + (allow_unwrap_in_tests: bool = false), } /// Search for the configuration file. @@ -375,3 +399,102 @@ pub fn read(path: &Path) -> TryConf { }; toml::from_str(&content).unwrap_or_else(TryConf::from_error) } + +const SEPARATOR_WIDTH: usize = 4; + +// Check whether the error is "unknown field" and, if so, list the available fields sorted and at +// least one per line, more if `CLIPPY_TERMINAL_WIDTH` is set and allows it. +pub fn format_error(error: Box) -> String { + let s = error.to_string(); + + if_chain! { + if error.downcast::().is_ok(); + if let Some((prefix, mut fields, suffix)) = parse_unknown_field_message(&s); + then { + use fmt::Write; + + fields.sort_unstable(); + + let (rows, column_widths) = calculate_dimensions(&fields); + + let mut msg = String::from(prefix); + for row in 0..rows { + write!(msg, "\n").unwrap(); + for (column, column_width) in column_widths.iter().copied().enumerate() { + let index = column * rows + row; + let field = fields.get(index).copied().unwrap_or_default(); + write!( + msg, + "{:separator_width$}{:field_width$}", + " ", + field, + separator_width = SEPARATOR_WIDTH, + field_width = column_width + ) + .unwrap(); + } + } + write!(msg, "\n{}", suffix).unwrap(); + msg + } else { + s + } + } +} + +// `parse_unknown_field_message` will become unnecessary if +// https://github.com/alexcrichton/toml-rs/pull/364 is merged. +fn parse_unknown_field_message(s: &str) -> Option<(&str, Vec<&str>, &str)> { + // An "unknown field" message has the following form: + // unknown field `UNKNOWN`, expected one of `FIELD0`, `FIELD1`, ..., `FIELDN` at line X column Y + // ^^ ^^^^ ^^ + if_chain! { + if s.starts_with("unknown field"); + let slices = s.split("`, `").collect::>(); + let n = slices.len(); + if n >= 2; + if let Some((prefix, first_field)) = slices[0].rsplit_once(" `"); + if let Some((last_field, suffix)) = slices[n - 1].split_once("` "); + then { + let fields = iter::once(first_field) + .chain(slices[1..n - 1].iter().copied()) + .chain(iter::once(last_field)) + .collect::>(); + Some((prefix, fields, suffix)) + } else { + None + } + } +} + +fn calculate_dimensions(fields: &[&str]) -> (usize, Vec) { + let columns = env::var("CLIPPY_TERMINAL_WIDTH") + .ok() + .and_then(|s| ::from_str(&s).ok()) + .map_or(1, |terminal_width| { + let max_field_width = fields.iter().map(|field| field.len()).max().unwrap(); + cmp::max(1, terminal_width / (SEPARATOR_WIDTH + max_field_width)) + }); + + let rows = (fields.len() + (columns - 1)) / columns; + + let column_widths = (0..columns) + .map(|column| { + if column < columns - 1 { + (0..rows) + .map(|row| { + let index = column * rows + row; + let field = fields.get(index).copied().unwrap_or_default(); + field.len() + }) + .max() + .unwrap() + } else { + // Avoid adding extra space to the last column. + 0 + } + }) + .collect::>(); + + (rows, column_widths) +} diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index 79e7410c3a838..ba1ff65479d60 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -12,7 +12,7 @@ use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; -#[allow(clippy::module_name_repetitions)] +#[expect(clippy::module_name_repetitions)] #[derive(Copy, Clone)] pub struct UselessVec { pub too_large_for_stack: u64, @@ -83,7 +83,7 @@ impl UselessVec { let snippet = match *vec_args { higher::VecArgs::Repeat(elem, len) => { if let Some((Constant::Int(len_constant), _)) = constant(cx, cx.typeck_results(), len) { - #[allow(clippy::cast_possible_truncation)] + #[expect(clippy::cast_possible_truncation)] if len_constant as u64 * size_of(cx, elem) > self.too_large_for_stack { return; } @@ -110,7 +110,6 @@ impl UselessVec { }, higher::VecArgs::Vec(args) => { if let Some(last) = args.iter().last() { - #[allow(clippy::cast_possible_truncation)] if args.len() as u64 * size_of(cx, last) > self.too_large_for_stack { return; } diff --git a/clippy_lints/src/vec_init_then_push.rs b/clippy_lints/src/vec_init_then_push.rs index fbf2b3e081b82..35db45e2b0c99 100644 --- a/clippy_lints/src/vec_init_then_push.rs +++ b/clippy_lints/src/vec_init_then_push.rs @@ -1,19 +1,30 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher::{get_vec_init_kind, VecInitKind}; use clippy_utils::source::snippet; -use clippy_utils::{path_to_local, path_to_local_id}; -use if_chain::if_chain; +use clippy_utils::visitors::for_each_local_use_after_expr; +use clippy_utils::{get_parent_expr, path_to_local_id}; +use core::ops::ControlFlow; use rustc_errors::Applicability; -use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Local, PatKind, Stmt, StmtKind}; +use rustc_hir::def::Res; +use rustc_hir::{ + BindingAnnotation, Block, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, Stmt, StmtKind, UnOp, +}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does /// Checks for calls to `push` immediately after creating a new `Vec`. /// + /// If the `Vec` is created using `with_capacity` this will only lint if the capacity is a + /// constant and the number of pushes is greater than or equal to the initial capacity. + /// + /// If the `Vec` is extended after the initial sequence of pushes and it was default initialized + /// then this will only lint after there were at least four pushes. This number may change in + /// the future. + /// /// ### Why is this bad? /// The `vec![]` macro is both more performant and easier to read than /// multiple `push` calls. @@ -43,26 +54,88 @@ pub struct VecInitThenPush { struct VecPushSearcher { local_id: HirId, init: VecInitKind, - lhs_is_local: bool, - lhs_span: Span, + lhs_is_let: bool, + let_ty_span: Option, + name: Symbol, err_span: Span, - found: u64, + found: u128, + last_push_expr: HirId, } impl VecPushSearcher { fn display_err(&self, cx: &LateContext<'_>) { - match self.init { + let required_pushes_before_extension = match self.init { _ if self.found == 0 => return, - VecInitKind::WithLiteralCapacity(x) if x > self.found => return, + VecInitKind::WithConstCapacity(x) if x > self.found => return, + VecInitKind::WithConstCapacity(x) => x, VecInitKind::WithExprCapacity(_) => return, - _ => (), + _ => 3, }; - let mut s = if self.lhs_is_local { + let mut needs_mut = false; + let res = for_each_local_use_after_expr(cx, self.local_id, self.last_push_expr, |e| { + let Some(parent) = get_parent_expr(cx, e) else { + return ControlFlow::Continue(()) + }; + let adjusted_ty = cx.typeck_results().expr_ty_adjusted(e); + let adjusted_mut = adjusted_ty.ref_mutability().unwrap_or(Mutability::Not); + needs_mut |= adjusted_mut == Mutability::Mut; + match parent.kind { + ExprKind::AddrOf(_, Mutability::Mut, _) => { + needs_mut = true; + return ControlFlow::Break(true); + }, + ExprKind::Unary(UnOp::Deref, _) | ExprKind::Index(..) if !needs_mut => { + let mut last_place = parent; + while let Some(parent) = get_parent_expr(cx, parent) { + if matches!(parent.kind, ExprKind::Unary(UnOp::Deref, _) | ExprKind::Field(..)) + || matches!(parent.kind, ExprKind::Index(e, _) if e.hir_id == last_place.hir_id) + { + last_place = parent; + } else { + break; + } + } + needs_mut |= cx.typeck_results().expr_ty_adjusted(last_place).ref_mutability() + == Some(Mutability::Mut) + || get_parent_expr(cx, last_place) + .map_or(false, |e| matches!(e.kind, ExprKind::AddrOf(_, Mutability::Mut, _))); + }, + ExprKind::MethodCall(_, [recv, ..], _) + if recv.hir_id == e.hir_id + && adjusted_mut == Mutability::Mut + && !adjusted_ty.peel_refs().is_slice() => + { + // No need to set `needs_mut` to true. The receiver will be either explicitly borrowed, or it will + // be implicitly borrowed via an adjustment. Both of these cases are already handled by this point. + return ControlFlow::Break(true); + }, + ExprKind::Assign(lhs, ..) if e.hir_id == lhs.hir_id => { + needs_mut = true; + return ControlFlow::Break(false); + }, + _ => (), + } + ControlFlow::Continue(()) + }); + + // Avoid allocating small `Vec`s when they'll be extended right after. + if res == ControlFlow::Break(true) && self.found <= required_pushes_before_extension { + return; + } + + let mut s = if self.lhs_is_let { String::from("let ") } else { String::new() }; - s.push_str(&snippet(cx, self.lhs_span, "..")); + if needs_mut { + s.push_str("mut "); + } + s.push_str(self.name.as_str()); + if let Some(span) = self.let_ty_span { + s.push_str(": "); + s.push_str(&snippet(cx, span, "_")); + } s.push_str(" = vec![..];"); span_lint_and_sugg( @@ -83,60 +156,63 @@ impl<'tcx> LateLintPass<'tcx> for VecInitThenPush { } fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { - if_chain! { - if !in_external_macro(cx.sess(), local.span); - if let Some(init) = local.init; - if let PatKind::Binding(BindingAnnotation::Mutable, id, _, None) = local.pat.kind; - if let Some(init_kind) = get_vec_init_kind(cx, init); - then { - self.searcher = Some(VecPushSearcher { - local_id: id, - init: init_kind, - lhs_is_local: true, - lhs_span: local.ty.map_or(local.pat.span, |t| local.pat.span.to(t.span)), - err_span: local.span, - found: 0, - }); - } + if let Some(init_expr) = local.init + && let PatKind::Binding(BindingAnnotation::Mutable, id, name, None) = local.pat.kind + && !in_external_macro(cx.sess(), local.span) + && let Some(init) = get_vec_init_kind(cx, init_expr) + && !matches!(init, VecInitKind::WithExprCapacity(_)) + { + self.searcher = Some(VecPushSearcher { + local_id: id, + init, + lhs_is_let: true, + name: name.name, + let_ty_span: local.ty.map(|ty| ty.span), + err_span: local.span, + found: 0, + last_push_expr: init_expr.hir_id, + }); } } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if self.searcher.is_none(); - if !in_external_macro(cx.sess(), expr.span); - if let ExprKind::Assign(left, right, _) = expr.kind; - if let Some(id) = path_to_local(left); - if let Some(init_kind) = get_vec_init_kind(cx, right); - then { - self.searcher = Some(VecPushSearcher { - local_id: id, - init: init_kind, - lhs_is_local: false, - lhs_span: left.span, - err_span: expr.span, - found: 0, - }); - } + if self.searcher.is_none() + && let ExprKind::Assign(left, right, _) = expr.kind + && let ExprKind::Path(QPath::Resolved(None, path)) = left.kind + && let [name] = &path.segments + && let Res::Local(id) = path.res + && !in_external_macro(cx.sess(), expr.span) + && let Some(init) = get_vec_init_kind(cx, right) + && !matches!(init, VecInitKind::WithExprCapacity(_)) + { + self.searcher = Some(VecPushSearcher { + local_id: id, + init, + lhs_is_let: false, + let_ty_span: None, + name: name.ident.name, + err_span: expr.span, + found: 0, + last_push_expr: expr.hir_id, + }); } } fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { if let Some(searcher) = self.searcher.take() { - if_chain! { - if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind; - if let ExprKind::MethodCall(path, [self_arg, _], _) = expr.kind; - if path_to_local_id(self_arg, searcher.local_id); - if path.ident.name.as_str() == "push"; - then { - self.searcher = Some(VecPushSearcher { - found: searcher.found + 1, - err_span: searcher.err_span.to(stmt.span), - .. searcher - }); - } else { - searcher.display_err(cx); - } + if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind + && let ExprKind::MethodCall(name, [self_arg, _], _) = expr.kind + && path_to_local_id(self_arg, searcher.local_id) + && name.ident.as_str() == "push" + { + self.searcher = Some(VecPushSearcher { + found: searcher.found + 1, + err_span: searcher.err_span.to(stmt.span), + last_push_expr: expr.hir_id, + .. searcher + }); + } else { + searcher.display_err(cx); } } } diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 54b93a20a057d..d2493c055a519 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -342,8 +342,6 @@ impl EarlyLintPass for Write { if let (Some(fmt_str), expr) = self.check_tts(cx, mac.args.inner_tokens(), true) { if fmt_str.symbol == kw::Empty { let mut applicability = Applicability::MachineApplicable; - // FIXME: remove this `#[allow(...)]` once the issue #5822 gets fixed - #[allow(clippy::option_if_let_else)] let suggestion = if let Some(e) = expr { snippet_with_applicability(cx, e.span, "v", &mut applicability) } else { @@ -528,7 +526,6 @@ impl Write { /// ```rust,ignore /// (Some("string to write: {}"), Some(buf)) /// ``` - #[allow(clippy::too_many_lines)] fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool) -> (Option, Option) { let mut parser = parser::Parser::new(&cx.sess().parse_sess, tts, false, None); let expr = if is_write { diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index 0b1fd95c3453d..c4e0b8448ab3f 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.62" +version = "0.1.63" edition = "2021" publish = false diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index 7919800483f52..b379f8c06c606 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -242,7 +242,7 @@ pub fn eq_item(l: &Item, r: &Item, mut eq_kind: impl FnMut(&K, &K) -> b eq_id(l.ident, r.ident) && over(&l.attrs, &r.attrs, eq_attr) && eq_vis(&l.vis, &r.vis) && eq_kind(&l.kind, &r.kind) } -#[allow(clippy::too_many_lines)] // Just a big match statement +#[expect(clippy::too_many_lines)] // Just a big match statement pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { use ItemKind::*; match (l, r) { diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index 904b1a05ccc30..49318849d5802 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -1,12 +1,11 @@ use rustc_ast::ast; +use rustc_ast::attr; use rustc_errors::Applicability; use rustc_session::Session; -use rustc_ast::attr; use rustc_span::sym; use std::str::FromStr; /// Deprecation status of attributes known by Clippy. -#[allow(dead_code)] pub enum DeprecationStatus { /// Attribute is deprecated Deprecated, @@ -158,4 +157,3 @@ pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool { .filter_map(ast::Attribute::meta_item_list) .any(|l| attr::list_contains_name(&l, sym::hidden)) } - diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index a80c7ee492958..9f162a117b2d1 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -13,7 +13,6 @@ use rustc_middle::ty::{self, EarlyBinder, FloatTy, ScalarInt, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_span::symbol::Symbol; use std::cmp::Ordering::{self, Equal}; -use std::convert::TryInto; use std::hash::{Hash, Hasher}; use std::iter; @@ -130,12 +129,10 @@ impl Constant { match (left, right) { (&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)), (&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)), - (&Self::Int(l), &Self::Int(r)) => { - if let ty::Int(int_ty) = *cmp_type.kind() { - Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty))) - } else { - Some(l.cmp(&r)) - } + (&Self::Int(l), &Self::Int(r)) => match *cmp_type.kind() { + ty::Int(int_ty) => Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty))), + ty::Uint(_) => Some(l.cmp(&r)), + _ => bug!("Not an int type"), }, (&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r), (&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r), @@ -352,7 +349,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } } - #[allow(clippy::cast_possible_wrap)] + #[expect(clippy::cast_possible_wrap)] fn constant_not(&self, o: &Constant, ty: Ty<'_>) -> Option { use self::Constant::{Bool, Int}; match *o { diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index b142397f71b9c..4e037d88494d7 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -77,7 +77,7 @@ pub fn span_lint(cx: &T, lint: &'static Lint, sp: impl Into( cx: &'a T, lint: &'static Lint, - span: Span, + span: impl Into, msg: &str, help_span: Option, help: &str, diff --git a/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs index a6ef6d79fc023..1a784b6cdda4c 100644 --- a/clippy_utils/src/eager_or_lazy.rs +++ b/clippy_utils/src/eager_or_lazy.rs @@ -96,7 +96,7 @@ fn fn_eagerness<'tcx>( } } -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggestion { struct V<'cx, 'tcx> { cx: &'cx LateContext<'tcx>, diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 2095fc966c5dc..1e0fc789af243 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -2,10 +2,11 @@ #![deny(clippy::missing_docs_in_private_items)] +use crate::consts::{constant_simple, Constant}; use crate::ty::is_type_diagnostic_item; use crate::{is_expn_of, match_def_path, paths}; use if_chain::if_chain; -use rustc_ast::ast::{self, LitKind}; +use rustc_ast::ast; use rustc_hir as hir; use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath}; use rustc_lint::LateContext; @@ -431,7 +432,7 @@ pub enum VecInitKind { /// `Vec::default()` or `Default::default()` Default, /// `Vec::with_capacity(123)` - WithLiteralCapacity(u64), + WithConstCapacity(u128), /// `Vec::with_capacity(slice.len())` WithExprCapacity(HirId), } @@ -449,15 +450,11 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) - return Some(VecInitKind::Default); } else if name.ident.name.as_str() == "with_capacity" { let arg = args.get(0)?; - if_chain! { - if let ExprKind::Lit(lit) = &arg.kind; - if let LitKind::Int(num, _) = lit.node; - then { - return Some(VecInitKind::WithLiteralCapacity(num.try_into().ok()?)); - } - } - return Some(VecInitKind::WithExprCapacity(arg.hir_id)); - } + return match constant_simple(cx, cx.typeck_results(), arg) { + Some(Constant::Int(num)) => Some(VecInitKind::WithConstCapacity(num)), + _ => Some(VecInitKind::WithExprCapacity(arg.hir_id)), + }; + }; }, ExprKind::Path(QPath::Resolved(_, path)) if match_def_path(cx, path.res.opt_def_id()?, &paths::DEFAULT_TRAIT_METHOD) diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index aa21f15ee5d9d..c440793b90e0e 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -64,7 +64,6 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { } } - #[allow(dead_code)] pub fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool { self.inter_expr().eq_block(left, right) } @@ -194,7 +193,7 @@ impl HirEqInterExpr<'_, '_, '_> { res } - #[allow(clippy::similar_names)] + #[expect(clippy::similar_names)] pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { if !self.inner.allow_side_effects && left.span.ctxt() != right.span.ctxt() { return false; @@ -361,7 +360,7 @@ impl HirEqInterExpr<'_, '_, '_> { } } - #[allow(clippy::similar_names)] + #[expect(clippy::similar_names)] fn eq_qpath(&mut self, left: &QPath<'_>, right: &QPath<'_>) -> bool { match (left, right) { (&QPath::Resolved(ref lty, lpath), &QPath::Resolved(ref rty, rpath)) => { @@ -407,7 +406,6 @@ impl HirEqInterExpr<'_, '_, '_> { left.ident.name == right.ident.name && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r)) } - #[allow(clippy::similar_names)] pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool { match (&left.kind, &right.kind) { (&TyKind::Slice(l_vec), &TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec), @@ -562,7 +560,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { std::mem::discriminant(&b.rules).hash(&mut self.s); } - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] pub fn hash_expr(&mut self, e: &Expr<'_>) { let simple_const = self .maybe_typeck_results diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 6db7f247a9925..adb37cc9d7510 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2,6 +2,7 @@ #![feature(control_flow_enum)] #![feature(let_else)] #![feature(let_chains)] +#![feature(lint_reasons)] #![feature(once_cell)] #![feature(rustc_private)] #![recursion_limit = "512"] @@ -35,7 +36,6 @@ extern crate rustc_typeck; #[macro_use] pub mod sym_helper; -#[allow(clippy::module_name_repetitions)] pub mod ast_utils; pub mod attrs; pub mod comparisons; @@ -67,6 +67,7 @@ use std::sync::{Mutex, MutexGuard}; use if_chain::if_chain; use rustc_ast::ast::{self, LitKind}; +use rustc_ast::Attribute; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::unhash::UnhashMap; use rustc_hir as hir; @@ -77,9 +78,9 @@ use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk}; use rustc_hir::{ def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr, ExprKind, FnDecl, - HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, - Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, - TraitRef, TyKind, UnOp, + HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Mutability, Node, + Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, + UnOp, }; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::place::PlaceBase; @@ -116,8 +117,8 @@ pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Opt None } -pub fn meets_msrv(msrv: Option<&RustcVersion>, lint_msrv: &RustcVersion) -> bool { - msrv.map_or(true, |msrv| msrv.meets(*lint_msrv)) +pub fn meets_msrv(msrv: Option, lint_msrv: RustcVersion) -> bool { + msrv.map_or(true, |msrv| msrv.meets(lint_msrv)) } #[macro_export] @@ -1554,14 +1555,14 @@ pub fn int_bits(tcx: TyCtxt<'_>, ity: rustc_ty::IntTy) -> u64 { Integer::from_int_ty(&tcx, ity).size().bits() } -#[allow(clippy::cast_possible_wrap)] +#[expect(clippy::cast_possible_wrap)] /// Turn a constant int byte representation into an i128 pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::IntTy) -> i128 { let amt = 128 - int_bits(tcx, ity); ((u as i128) << amt) >> amt } -#[allow(clippy::cast_sign_loss)] +#[expect(clippy::cast_sign_loss)] /// clip unused bytes pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: rustc_ty::IntTy) -> u128 { let amt = 128 - int_bits(tcx, ity); @@ -2123,6 +2124,27 @@ pub fn is_in_test_function(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { }) } +/// Checks if the item containing the given `HirId` has `#[cfg(test)]` attribute applied +/// +/// Note: Add `// compile-flags: --test` to UI tests with a `#[cfg(test)]` function +pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { + fn is_cfg_test(attr: &Attribute) -> bool { + if attr.has_name(sym::cfg) + && let Some(items) = attr.meta_item_list() + && let [item] = &*items + && item.has_name(sym::test) + { + true + } else { + false + } + } + tcx.hir() + .parent_iter(id) + .flat_map(|(parent_id, _)| tcx.hir().attrs(parent_id)) + .any(is_cfg_test) +} + /// Checks whether item either has `test` attribute applied, or /// is a module with `test` in its name. /// diff --git a/clippy_utils/src/numeric_literal.rs b/clippy_utils/src/numeric_literal.rs index b92d42e83232c..3fb5415ce0299 100644 --- a/clippy_utils/src/numeric_literal.rs +++ b/clippy_utils/src/numeric_literal.rs @@ -1,7 +1,7 @@ use rustc_ast::ast::{Lit, LitFloatType, LitIntType, LitKind}; use std::iter; -#[derive(Debug, PartialEq, Copy, Clone)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum Radix { Binary, Octal, diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 60971fb716dbd..9b9cbff2d1462 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -43,9 +43,9 @@ pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"]; pub const FROM_STR_METHOD: [&str; 5] = ["core", "str", "traits", "FromStr", "from_str"]; pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncReadExt"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const FUTURES_IO_ASYNCWRITEEXT: [&str; 3] = ["futures_util", "io", "AsyncWriteExt"]; pub const HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"]; pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"]; @@ -63,7 +63,7 @@ pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"]; pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"]; pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"]; pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"]; #[cfg(feature = "internal")] pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; @@ -117,17 +117,17 @@ pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"]; pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"]; pub const REFCELL_REFMUT: [&str; 3] = ["core", "cell", "RefMut"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"]; /// Preferably use the diagnostic item `sym::Result` where possible pub const RESULT: [&str; 3] = ["core", "result", "Result"]; @@ -169,9 +169,9 @@ pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"]; pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"]; pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const TOKIO_IO_ASYNCREADEXT: [&str; 5] = ["tokio", "io", "util", "async_read_ext", "AsyncReadExt"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const TOKIO_IO_ASYNCWRITEEXT: [&str; 5] = ["tokio", "io", "util", "async_write_ext", "AsyncWriteExt"]; pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"]; pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"]; diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 66d373a1bf81a..a6d7042fabc26 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -18,7 +18,7 @@ use std::borrow::Cow; type McfResult = Result<(), (Span, Cow<'static, str>)>; -pub fn is_min_const_fn<'a, 'tcx>(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&RustcVersion>) -> McfResult { +pub fn is_min_const_fn<'a, 'tcx>(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option) -> McfResult { let def_id = body.source.def_id(); let mut current = def_id; loop { @@ -268,7 +268,7 @@ fn check_terminator<'a, 'tcx>( tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Terminator<'tcx>, - msrv: Option<&RustcVersion>, + msrv: Option, ) -> McfResult { let span = terminator.source_info.span; match &terminator.kind { @@ -352,7 +352,7 @@ fn check_terminator<'a, 'tcx>( } } -fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<&RustcVersion>) -> bool { +fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option) -> bool { tcx.is_const_fn(def_id) && tcx.lookup_const_stability(def_id).map_or(true, |const_stab| { if let rustc_attr::StabilityLevel::Stable { since } = const_stab.level { @@ -361,7 +361,7 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<&RustcVersion>) -> b // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262. crate::meets_msrv( msrv, - &RustcVersion::parse(since.as_str()) + RustcVersion::parse(since.as_str()) .expect("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted"), ) } else { diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index c69a3d8d2a15e..04ef2f57447c6 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -137,7 +137,7 @@ pub fn position_before_rarrow(s: &str) -> Option { } /// Reindent a multiline string with possibility of ignoring the first line. -#[allow(clippy::needless_pass_by_value)] +#[expect(clippy::needless_pass_by_value)] pub fn reindent_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option) -> Cow<'_, str> { let s_space = reindent_multiline_inner(&s, ignore_first, indent, ' '); let s_tab = reindent_multiline_inner(&s_space, ignore_first, indent, '\t'); diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index db5299c2c05f4..4f3757f1ec673 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -17,7 +17,6 @@ use rustc_middle::ty; use rustc_span::source_map::{BytePos, CharPos, Pos, Span, SyntaxContext}; use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use std::borrow::Cow; -use std::convert::TryInto; use std::fmt::{Display, Write as _}; use std::iter; use std::ops::{Add, Neg, Not, Sub}; @@ -50,7 +49,7 @@ impl Display for Sugg<'_> { } } -#[allow(clippy::wrong_self_convention)] // ok, because of the function `as_ty` method +#[expect(clippy::wrong_self_convention)] // ok, because of the function `as_ty` method impl<'a> Sugg<'a> { /// Prepare a suggestion from an expression. pub fn hir_opt(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { @@ -318,7 +317,6 @@ impl<'a> Sugg<'a> { /// Convenience method to create the `..` or `...` /// suggestion. - #[allow(dead_code)] pub fn range(self, end: &Self, limit: ast::RangeLimits) -> Sugg<'static> { match limit { ast::RangeLimits::HalfOpen => make_assoc(AssocOp::DotDot, &self, end), @@ -886,7 +884,6 @@ impl<'tcx> DerefDelegate<'_, 'tcx> { impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} - #[allow(clippy::too_many_lines)] fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) { if let PlaceBase::Local(id) = cmt.place.base { let map = self.cx.tcx.hir(); diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index b09eb8c6cd10b..07d3d2807634f 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -603,7 +603,7 @@ impl core::ops::Add for EnumValue { } /// Attempts to read the given constant as though it were an an enum value. -#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)] +#[expect(clippy::cast_possible_truncation, clippy::cast_possible_wrap)] pub fn read_explicit_enum_value(tcx: TyCtxt<'_>, id: DefId) -> Option { if let Ok(ConstValue::Scalar(Scalar::Int(value))) = tcx.const_eval_poly(id) { match tcx.type_of(id).kind() { diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index b7b9d54d0b2c1..9819778540cc9 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -44,7 +44,6 @@ struct MutVarsDelegate { } impl<'tcx> MutVarsDelegate { - #[allow(clippy::similar_names)] fn update(&mut self, cat: &PlaceWithHirId<'tcx>) { match cat.place.base { PlaceBase::Local(id) => { diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index c00bc2bd213f9..b6c8f1d516e55 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -1,4 +1,4 @@ -use crate::path_to_local_id; +use crate::{get_enclosing_block, path_to_local_id}; use core::ops::ControlFlow; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; @@ -436,3 +436,61 @@ pub fn for_each_value_source<'tcx, B>( _ => f(e), } } + +/// Runs the given function for each path expression referencing the given local which occur after +/// the given expression. +pub fn for_each_local_use_after_expr<'tcx, B>( + cx: &LateContext<'tcx>, + local_id: HirId, + expr_id: HirId, + f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow, +) -> ControlFlow { + struct V<'cx, 'tcx, F, B> { + cx: &'cx LateContext<'tcx>, + local_id: HirId, + expr_id: HirId, + found: bool, + res: ControlFlow, + f: F, + } + impl<'cx, 'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow, B> Visitor<'tcx> for V<'cx, 'tcx, F, B> { + type NestedFilter = nested_filter::OnlyBodies; + fn nested_visit_map(&mut self) -> Self::Map { + self.cx.tcx.hir() + } + + fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) { + if !self.found { + if e.hir_id == self.expr_id { + self.found = true; + } else { + walk_expr(self, e); + } + return; + } + if self.res.is_break() { + return; + } + if path_to_local_id(e, self.local_id) { + self.res = (self.f)(e); + } else { + walk_expr(self, e); + } + } + } + + if let Some(b) = get_enclosing_block(cx, local_id) { + let mut v = V { + cx, + local_id, + expr_id, + found: false, + res: ControlFlow::Continue(()), + f, + }; + v.visit_block(b); + v.res + } else { + ControlFlow::Continue(()) + } +} diff --git a/doc/adding_lints.md b/doc/adding_lints.md index 307cf2f3a9047..e8f0c338fd58a 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -28,7 +28,7 @@ because that's clearly a non-descriptive name. - [Debugging](#debugging) - [PR Checklist](#pr-checklist) - [Adding configuration to a lint](#adding-configuration-to-a-lint) - - [Cheatsheet](#cheatsheet) + - [Cheat Sheet](#cheat-sheet) ## Setup @@ -432,7 +432,7 @@ The project's MSRV can then be matched against the feature MSRV in the LintPass using the `meets_msrv` utility function. ``` rust -if !meets_msrv(self.msrv.as_ref(), &msrvs::STR_STRIP_PREFIX) { +if !meets_msrv(self.msrv, msrvs::STR_STRIP_PREFIX) { return; } ``` @@ -649,14 +649,14 @@ in the following steps: with the configuration value and a rust file that should be linted by Clippy. The test can otherwise be written as usual. -## Cheatsheet +## Cheat Sheet Here are some pointers to things you are likely going to need for every lint: * [Clippy utils][utils] - Various helper functions. Maybe the function you need is already in here ([`is_type_diagnostic_item`], [`implements_trait`], [`snippet`], etc) * [Clippy diagnostics][diagnostics] -* [The `if_chain` macro][if_chain] +* [Let chains][let-chains] * [`from_expansion`][from_expansion] and [`in_external_macro`][in_external_macro] * [`Span`][span] * [`Applicability`][applicability] @@ -684,7 +684,7 @@ don't hesitate to ask on [Zulip] or in the issue/PR. [`is_type_diagnostic_item`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.is_type_diagnostic_item.html [`implements_trait`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.implements_trait.html [`snippet`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/source/fn.snippet.html -[if_chain]: https://docs.rs/if_chain/*/if_chain/ +[let-chains]: https://github.com/rust-lang/rust/pull/94927 [from_expansion]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html#method.from_expansion [in_external_macro]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/lint/fn.in_external_macro.html [span]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html diff --git a/doc/common_tools_writing_lints.md b/doc/common_tools_writing_lints.md index 828bf4cbef948..131ac3c3611e8 100644 --- a/doc/common_tools_writing_lints.md +++ b/doc/common_tools_writing_lints.md @@ -62,16 +62,14 @@ Starting with an `expr`, you can check whether it is calling a specific method ` ```rust impl<'tcx> LateLintPass<'tcx> for MyStructLint { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if_chain! { - // Check our expr is calling a method - if let hir::ExprKind::MethodCall(path, _, [_self_arg, ..]) = &expr.kind; + // Check our expr is calling a method + if let hir::ExprKind::MethodCall(path, _, [_self_arg, ..]) = &expr.kind // Check the name of this method is `some_method` - if path.ident.name == sym!(some_method); + && path.ident.name == sym!(some_method) // Optionally, check the type of the self argument. // - See "Checking for a specific type" - then { + { // ... - } } } } @@ -165,18 +163,16 @@ use clippy_utils::{is_type_diagnostic_item, return_ty}; impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { - if_chain! { - // Check if item is a method/function - if let ImplItemKind::Fn(ref signature, _) = impl_item.kind; + // Check if item is a method/function + if let ImplItemKind::Fn(ref signature, _) = impl_item.kind // Check the method is named `some_method` - if impl_item.ident.name == sym!(some_method); + && impl_item.ident.name == sym!(some_method) // We can also check it has a parameter `self` - if signature.decl.implicit_self.has_implicit_self(); + && signature.decl.implicit_self.has_implicit_self() // We can go further and even check if its return type is `String` - if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(string_type)); - then { - // ... - } + && is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(string_type)) + { + // ... } } } diff --git a/lintcheck/Cargo.toml b/lintcheck/Cargo.toml index c694037021a5e..e63f65ce2f75d 100644 --- a/lintcheck/Cargo.toml +++ b/lintcheck/Cargo.toml @@ -6,16 +6,15 @@ readme = "README.md" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust-clippy" categories = ["development-tools"] -edition = "2018" +edition = "2021" publish = false [dependencies] +cargo_metadata = "0.14" clap = "2.33" flate2 = "1.0" -fs_extra = "1.2" rayon = "1.5.1" serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" tar = "0.4" toml = "0.5" ureq = "2.2" diff --git a/lintcheck/lintcheck_crates.toml b/lintcheck/lintcheck_crates.toml index dfee28f1a8712..4fbae8614ca3d 100644 --- a/lintcheck/lintcheck_crates.toml +++ b/lintcheck/lintcheck_crates.toml @@ -24,7 +24,7 @@ unicode-xid = {name = "unicode-xid", versions = ['0.2.1']} anyhow = {name = "anyhow", versions = ['1.0.38']} async-trait = {name = "async-trait", versions = ['0.1.42']} cxx = {name = "cxx", versions = ['1.0.32']} -ryu = {name = "ryu", version = ['1.0.5']} +ryu = {name = "ryu", versions = ['1.0.5']} serde_yaml = {name = "serde_yaml", versions = ['0.8.17']} thiserror = {name = "thiserror", versions = ['1.0.24']} # some embark crates, there are other interesting crates but diff --git a/lintcheck/src/config.rs b/lintcheck/src/config.rs new file mode 100644 index 0000000000000..de32b48436019 --- /dev/null +++ b/lintcheck/src/config.rs @@ -0,0 +1,140 @@ +use clap::{App, Arg, ArgMatches}; +use std::env; +use std::path::PathBuf; + +fn get_clap_config<'a>() -> ArgMatches<'a> { + App::new("lintcheck") + .about("run clippy on a set of crates and check output") + .arg( + Arg::with_name("only") + .takes_value(true) + .value_name("CRATE") + .long("only") + .help("Only process a single crate of the list"), + ) + .arg( + Arg::with_name("crates-toml") + .takes_value(true) + .value_name("CRATES-SOURCES-TOML-PATH") + .long("crates-toml") + .help("Set the path for a crates.toml where lintcheck should read the sources from"), + ) + .arg( + Arg::with_name("threads") + .takes_value(true) + .value_name("N") + .short("j") + .long("jobs") + .help("Number of threads to use, 0 automatic choice"), + ) + .arg( + Arg::with_name("fix") + .long("--fix") + .help("Runs cargo clippy --fix and checks if all suggestions apply"), + ) + .arg( + Arg::with_name("filter") + .long("--filter") + .takes_value(true) + .multiple(true) + .value_name("clippy_lint_name") + .help("Apply a filter to only collect specified lints, this also overrides `allow` attributes"), + ) + .arg( + Arg::with_name("markdown") + .long("--markdown") + .help("Change the reports table to use markdown links"), + ) + .get_matches() +} + +#[derive(Debug)] +pub(crate) struct LintcheckConfig { + /// max number of jobs to spawn (default 1) + pub max_jobs: usize, + /// we read the sources to check from here + pub sources_toml_path: PathBuf, + /// we save the clippy lint results here + pub lintcheck_results_path: PathBuf, + /// Check only a specified package + pub only: Option, + /// whether to just run --fix and not collect all the warnings + pub fix: bool, + /// A list of lints that this lintcheck run should focus on + pub lint_filter: Vec, + /// Indicate if the output should support markdown syntax + pub markdown: bool, +} + +impl LintcheckConfig { + pub fn new() -> Self { + let clap_config = get_clap_config(); + + // first, check if we got anything passed via the LINTCHECK_TOML env var, + // if not, ask clap if we got any value for --crates-toml + // if not, use the default "lintcheck/lintcheck_crates.toml" + let sources_toml = env::var("LINTCHECK_TOML").unwrap_or_else(|_| { + clap_config + .value_of("crates-toml") + .clone() + .unwrap_or("lintcheck/lintcheck_crates.toml") + .to_string() + }); + + let markdown = clap_config.is_present("markdown"); + let sources_toml_path = PathBuf::from(sources_toml); + + // for the path where we save the lint results, get the filename without extension (so for + // wasd.toml, use "wasd"...) + let filename: PathBuf = sources_toml_path.file_stem().unwrap().into(); + let lintcheck_results_path = PathBuf::from(format!( + "lintcheck-logs/{}_logs.{}", + filename.display(), + if markdown { "md" } else { "txt" } + )); + + // look at the --threads arg, if 0 is passed, ask rayon rayon how many threads it would spawn and + // use half of that for the physical core count + // by default use a single thread + let max_jobs = match clap_config.value_of("threads") { + Some(threads) => { + let threads: usize = threads + .parse() + .unwrap_or_else(|_| panic!("Failed to parse '{}' to a digit", threads)); + if threads == 0 { + // automatic choice + // Rayon seems to return thread count so half that for core count + (rayon::current_num_threads() / 2) as usize + } else { + threads + } + }, + // no -j passed, use a single thread + None => 1, + }; + + let lint_filter: Vec = clap_config + .values_of("filter") + .map(|iter| { + iter.map(|lint_name| { + let mut filter = lint_name.replace('_', "-"); + if !filter.starts_with("clippy::") { + filter.insert_str(0, "clippy::"); + } + filter + }) + .collect() + }) + .unwrap_or_default(); + + LintcheckConfig { + max_jobs, + sources_toml_path, + lintcheck_results_path, + only: clap_config.value_of("only").map(String::from), + fix: clap_config.is_present("fix"), + lint_filter, + markdown, + } + } +} diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index 816efbdaedf36..dff9d27db0a6c 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -7,23 +7,25 @@ #![allow(clippy::collapsible_else_if)] -use std::ffi::OsStr; +mod config; + +use config::LintcheckConfig; + +use std::collections::HashMap; +use std::env; use std::fmt::Write as _; +use std::fs::write; +use std::io::ErrorKind; +use std::path::{Path, PathBuf}; use std::process::Command; use std::sync::atomic::{AtomicUsize, Ordering}; -use std::{collections::HashMap, io::ErrorKind}; -use std::{ - env, - fs::write, - path::{Path, PathBuf}, - thread, - time::Duration, -}; - -use clap::{App, Arg, ArgMatches}; +use std::thread; +use std::time::Duration; + +use cargo_metadata::diagnostic::DiagnosticLevel; +use cargo_metadata::Message; use rayon::prelude::*; use serde::{Deserialize, Serialize}; -use serde_json::Value; use walkdir::{DirEntry, WalkDir}; #[cfg(not(windows))] @@ -93,37 +95,67 @@ struct Crate { #[derive(Debug)] struct ClippyWarning { crate_name: String, - crate_version: String, file: String, - line: String, - column: String, - linttype: String, + line: usize, + column: usize, + lint_type: String, message: String, is_ice: bool, } #[allow(unused)] impl ClippyWarning { + fn new(cargo_message: Message, krate: &Crate) -> Option { + let diag = match cargo_message { + Message::CompilerMessage(message) => message.message, + _ => return None, + }; + + let lint_type = diag.code?.code; + if !(lint_type.contains("clippy") || diag.message.contains("clippy")) + || diag.message.contains("could not read cargo metadata") + { + return None; + } + + let span = diag.spans.into_iter().find(|span| span.is_primary)?; + + let file = match Path::new(&span.file_name).strip_prefix(env!("CARGO_HOME")) { + Ok(stripped) => format!("$CARGO_HOME/{}", stripped.display()), + Err(_) => format!( + "target/lintcheck/sources/{}-{}/{}", + krate.name, krate.version, span.file_name + ), + }; + + Some(Self { + crate_name: krate.name.clone(), + file, + line: span.line_start, + column: span.column_start, + lint_type, + message: diag.message, + is_ice: diag.level == DiagnosticLevel::Ice, + }) + } + fn to_output(&self, markdown: bool) -> String { - let file = format!("{}-{}/{}", &self.crate_name, &self.crate_version, &self.file); - let file_with_pos = format!("{}:{}:{}", &file, &self.line, &self.column); + let file_with_pos = format!("{}:{}:{}", &self.file, &self.line, &self.column); if markdown { - let lint = format!("`{}`", self.linttype); + let lint = format!("`{}`", self.lint_type); + + let mut file = self.file.clone(); + if !file.starts_with('$') { + file.insert_str(0, "../"); + } let mut output = String::from("| "); - let _ = write!( - output, - "[`{}`](../target/lintcheck/sources/{}#L{})", - file_with_pos, file, self.line - ); + let _ = write!(output, "[`{}`]({}#L{})", file_with_pos, file, self.line); let _ = write!(output, r#" | {:<50} | "{}" |"#, lint, self.message); output.push('\n'); output } else { - format!( - "target/lintcheck/sources/{} {} \"{}\"\n", - file_with_pos, self.linttype, self.message - ) + format!("{} {} \"{}\"\n", file_with_pos, self.lint_type, self.message) } } } @@ -278,18 +310,17 @@ impl Crate { &self, cargo_clippy_path: &Path, target_dir_index: &AtomicUsize, - thread_limit: usize, total_crates_to_lint: usize, - fix: bool, + config: &LintcheckConfig, lint_filter: &Vec, ) -> Vec { // advance the atomic index by one let index = target_dir_index.fetch_add(1, Ordering::SeqCst); // "loop" the index within 0..thread_limit - let thread_index = index % thread_limit; + let thread_index = index % config.max_jobs; let perc = (index * 100) / total_crates_to_lint; - if thread_limit == 1 { + if config.max_jobs == 1 { println!( "{}/{} {}% Linting {} {}", index, total_crates_to_lint, perc, &self.name, &self.version @@ -305,7 +336,7 @@ impl Crate { let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir"); - let mut args = if fix { + let mut args = if config.fix { vec!["--fix", "--"] } else { vec!["--", "--message-format=json", "--"] @@ -356,7 +387,7 @@ impl Crate { ); } - if fix { + if config.fix { if let Some(stderr) = stderr .lines() .find(|line| line.contains("failed to automatically apply fixes suggested by rustc to crate")) @@ -371,127 +402,15 @@ impl Crate { return Vec::new(); } - let output_lines = stdout.lines(); - let warnings: Vec = output_lines - .into_iter() - // get all clippy warnings and ICEs - .filter(|line| filter_clippy_warnings(&line)) - .map(|json_msg| parse_json_message(json_msg, &self)) + // get all clippy warnings and ICEs + let warnings: Vec = Message::parse_stream(stdout.as_bytes()) + .filter_map(|msg| ClippyWarning::new(msg.unwrap(), &self)) .collect(); warnings } } -#[derive(Debug)] -struct LintcheckConfig { - /// max number of jobs to spawn (default 1) - max_jobs: usize, - /// we read the sources to check from here - sources_toml_path: PathBuf, - /// we save the clippy lint results here - lintcheck_results_path: PathBuf, - /// whether to just run --fix and not collect all the warnings - fix: bool, - /// A list of lints that this lintcheck run should focus on - lint_filter: Vec, - /// Indicate if the output should support markdown syntax - markdown: bool, -} - -impl LintcheckConfig { - fn from_clap(clap_config: &ArgMatches) -> Self { - // first, check if we got anything passed via the LINTCHECK_TOML env var, - // if not, ask clap if we got any value for --crates-toml - // if not, use the default "lintcheck/lintcheck_crates.toml" - let sources_toml = env::var("LINTCHECK_TOML").unwrap_or_else(|_| { - clap_config - .value_of("crates-toml") - .clone() - .unwrap_or("lintcheck/lintcheck_crates.toml") - .to_string() - }); - - let markdown = clap_config.is_present("markdown"); - let sources_toml_path = PathBuf::from(sources_toml); - - // for the path where we save the lint results, get the filename without extension (so for - // wasd.toml, use "wasd"...) - let filename: PathBuf = sources_toml_path.file_stem().unwrap().into(); - let lintcheck_results_path = PathBuf::from(format!( - "lintcheck-logs/{}_logs.{}", - filename.display(), - if markdown { "md" } else { "txt" } - )); - - // look at the --threads arg, if 0 is passed, ask rayon rayon how many threads it would spawn and - // use half of that for the physical core count - // by default use a single thread - let max_jobs = match clap_config.value_of("threads") { - Some(threads) => { - let threads: usize = threads - .parse() - .unwrap_or_else(|_| panic!("Failed to parse '{}' to a digit", threads)); - if threads == 0 { - // automatic choice - // Rayon seems to return thread count so half that for core count - (rayon::current_num_threads() / 2) as usize - } else { - threads - } - }, - // no -j passed, use a single thread - None => 1, - }; - let fix: bool = clap_config.is_present("fix"); - let lint_filter: Vec = clap_config - .values_of("filter") - .map(|iter| { - iter.map(|lint_name| { - let mut filter = lint_name.replace('_', "-"); - if !filter.starts_with("clippy::") { - filter.insert_str(0, "clippy::"); - } - filter - }) - .collect() - }) - .unwrap_or_default(); - - LintcheckConfig { - max_jobs, - sources_toml_path, - lintcheck_results_path, - fix, - lint_filter, - markdown, - } - } -} - -/// takes a single json-formatted clippy warnings and returns true (we are interested in that line) -/// or false (we aren't) -fn filter_clippy_warnings(line: &str) -> bool { - // we want to collect ICEs because clippy might have crashed. - // these are summarized later - if line.contains("internal compiler error: ") { - return true; - } - // in general, we want all clippy warnings - // however due to some kind of bug, sometimes there are absolute paths - // to libcore files inside the message - // or we end up with cargo-metadata output (https://github.com/rust-lang/rust-clippy/issues/6508) - - // filter out these message to avoid unnecessary noise in the logs - if line.contains("clippy::") - && !(line.contains("could not read cargo metadata") - || (line.contains(".rustup") && line.contains("toolchains"))) - { - return true; - } - false -} - /// Builds clippy inside the repo to make sure we have a clippy executable we can use. fn build_clippy() { let status = Command::new("cargo") @@ -527,10 +446,8 @@ fn read_crates(toml_path: &Path) -> Vec { path: PathBuf::from(path), options: tk.options.clone(), }); - } - - // if we have multiple versions, save each one - if let Some(ref versions) = tk.versions { + } else if let Some(ref versions) = tk.versions { + // if we have multiple versions, save each one versions.iter().for_each(|ver| { crate_sources.push(CrateSource::CratesIo { name: tk.name.clone(), @@ -538,16 +455,18 @@ fn read_crates(toml_path: &Path) -> Vec { options: tk.options.clone(), }); }) - } - // otherwise, we should have a git source - if tk.git_url.is_some() && tk.git_hash.is_some() { + } else if tk.git_url.is_some() && tk.git_hash.is_some() { + // otherwise, we should have a git source crate_sources.push(CrateSource::Git { name: tk.name.clone(), url: tk.git_url.clone().unwrap(), commit: tk.git_hash.clone().unwrap(), options: tk.options.clone(), }); + } else { + panic!("Invalid crate source: {tk:?}"); } + // if we have a version as well as a git data OR only one git data, something is funky if tk.versions.is_some() && (tk.git_url.is_some() || tk.git_hash.is_some()) || tk.git_hash.is_some() != tk.git_url.is_some() @@ -568,57 +487,13 @@ fn read_crates(toml_path: &Path) -> Vec { crate_sources } -/// Parse the json output of clippy and return a `ClippyWarning` -fn parse_json_message(json_message: &str, krate: &Crate) -> ClippyWarning { - let jmsg: Value = serde_json::from_str(&json_message).unwrap_or_else(|e| panic!("Failed to parse json:\n{:?}", e)); - - let file: String = jmsg["message"]["spans"][0]["file_name"] - .to_string() - .trim_matches('"') - .into(); - - let file = if file.contains(".cargo") { - // if we deal with macros, a filename may show the origin of a macro which can be inside a dep from - // the registry. - // don't show the full path in that case. - - // /home/matthias/.cargo/registry/src/github.1485827954.workers.dev-1ecc6299db9ec823/syn-1.0.63/src/custom_keyword.rs - let path = PathBuf::from(file); - let mut piter = path.iter(); - // consume all elements until we find ".cargo", so that "/home/matthias" is skipped - let _: Option<&OsStr> = piter.find(|x| x == &std::ffi::OsString::from(".cargo")); - // collect the remaining segments - let file = piter.collect::(); - format!("{}", file.display()) - } else { - file - }; - - ClippyWarning { - crate_name: krate.name.to_string(), - crate_version: krate.version.to_string(), - file, - line: jmsg["message"]["spans"][0]["line_start"] - .to_string() - .trim_matches('"') - .into(), - column: jmsg["message"]["spans"][0]["text"][0]["highlight_start"] - .to_string() - .trim_matches('"') - .into(), - linttype: jmsg["message"]["code"]["code"].to_string().trim_matches('"').into(), - message: jmsg["message"]["message"].to_string().trim_matches('"').into(), - is_ice: json_message.contains("internal compiler error: "), - } -} - /// Generate a short list of occurring lints-types and their count fn gather_stats(clippy_warnings: &[ClippyWarning]) -> (String, HashMap<&String, usize>) { // count lint type occurrences let mut counter: HashMap<&String, usize> = HashMap::new(); clippy_warnings .iter() - .for_each(|wrn| *counter.entry(&wrn.linttype).or_insert(0) += 1); + .for_each(|wrn| *counter.entry(&wrn.lint_type).or_insert(0) += 1); // collect into a tupled list for sorting let mut stats: Vec<(&&String, &usize)> = counter.iter().map(|(lint, count)| (lint, count)).collect(); @@ -667,22 +542,14 @@ fn lintcheck_needs_rerun(lintcheck_logs_path: &Path) -> bool { logs_modified < clippy_modified } -/// lintchecks `main()` function -/// -/// # Panics -/// -/// This function panics if the clippy binaries don't exist -/// or if lintcheck is executed from the wrong directory (aka none-repo-root) -pub fn main() { +fn main() { // assert that we launch lintcheck from the repo root (via cargo lintcheck) if std::fs::metadata("lintcheck/Cargo.toml").is_err() { eprintln!("lintcheck needs to be run from clippys repo root!\nUse `cargo lintcheck` alternatively."); std::process::exit(3); } - let clap_config = &get_clap_config(); - - let config = LintcheckConfig::from_clap(clap_config); + let config = LintcheckConfig::new(); println!("Compiling clippy..."); build_clippy(); @@ -736,76 +603,46 @@ pub fn main() { }) .collect(); - let clippy_warnings: Vec = if let Some(only_one_crate) = clap_config.value_of("only") { - // if we don't have the specified crate in the .toml, throw an error - if !crates.iter().any(|krate| { - let name = match krate { - CrateSource::CratesIo { name, .. } | CrateSource::Git { name, .. } | CrateSource::Path { name, .. } => { - name - }, - }; - name == only_one_crate - }) { - eprintln!( - "ERROR: could not find crate '{}' in lintcheck/lintcheck_crates.toml", - only_one_crate - ); - std::process::exit(1); - } + let crates: Vec = crates + .into_iter() + .filter(|krate| { + if let Some(only_one_crate) = &config.only { + let name = match krate { + CrateSource::CratesIo { name, .. } + | CrateSource::Git { name, .. } + | CrateSource::Path { name, .. } => name, + }; - // only check a single crate that was passed via cmdline - crates - .into_iter() - .map(|krate| krate.download_and_extract()) - .filter(|krate| krate.name == only_one_crate) - .flat_map(|krate| { - krate.run_clippy_lints(&cargo_clippy_path, &AtomicUsize::new(0), 1, 1, config.fix, &lint_filter) - }) - .collect() - } else { - if config.max_jobs > 1 { - // run parallel with rayon - - // Ask rayon for thread count. Assume that half of that is the number of physical cores - // Use one target dir for each core so that we can run N clippys in parallel. - // We need to use different target dirs because cargo would lock them for a single build otherwise, - // killing the parallelism. However this also means that deps will only be reused half/a - // quarter of the time which might result in a longer wall clock runtime - - // This helps when we check many small crates with dep-trees that don't have a lot of branches in - // order to achieve some kind of parallelism - - // by default, use a single thread - let num_cpus = config.max_jobs; - let num_crates = crates.len(); - - // check all crates (default) - crates - .into_par_iter() - .map(|krate| krate.download_and_extract()) - .flat_map(|krate| { - krate.run_clippy_lints( - &cargo_clippy_path, - &counter, - num_cpus, - num_crates, - config.fix, - &lint_filter, - ) - }) - .collect() - } else { - // run sequential - let num_crates = crates.len(); - crates - .into_iter() - .map(|krate| krate.download_and_extract()) - .flat_map(|krate| { - krate.run_clippy_lints(&cargo_clippy_path, &counter, 1, num_crates, config.fix, &lint_filter) - }) - .collect() - } - }; + name == only_one_crate + } else { + true + } + }) + .map(|krate| krate.download_and_extract()) + .collect(); + + if crates.is_empty() { + eprintln!( + "ERROR: could not find crate '{}' in lintcheck/lintcheck_crates.toml", + config.only.unwrap(), + ); + std::process::exit(1); + } + + // run parallel with rayon + + // This helps when we check many small crates with dep-trees that don't have a lot of branches in + // order to achieve some kind of parallelism + + rayon::ThreadPoolBuilder::new() + .num_threads(config.max_jobs) + .build_global() + .unwrap(); + + let clippy_warnings: Vec = crates + .par_iter() + .flat_map(|krate| krate.run_clippy_lints(&cargo_clippy_path, &counter, crates.len(), &config, &lint_filter)) + .collect(); // if we are in --fix mode, don't change the log files, terminate here if config.fix { @@ -837,7 +674,7 @@ pub fn main() { text.push_str("| file | lint | message |\n"); text.push_str("| --- | --- | --- |\n"); } - write!(text, "{}", all_msgs.join("")); + write!(text, "{}", all_msgs.join("")).unwrap(); text.push_str("\n\n### ICEs:\n"); for (cratename, msg) in ices.iter() { let _ = write!(text, "{}: '{}'", cratename, msg); @@ -949,75 +786,10 @@ fn create_dirs(krate_download_dir: &Path, extract_dir: &Path) { }); } -fn get_clap_config<'a>() -> ArgMatches<'a> { - App::new("lintcheck") - .about("run clippy on a set of crates and check output") - .arg( - Arg::with_name("only") - .takes_value(true) - .value_name("CRATE") - .long("only") - .help("only process a single crate of the list"), - ) - .arg( - Arg::with_name("crates-toml") - .takes_value(true) - .value_name("CRATES-SOURCES-TOML-PATH") - .long("crates-toml") - .help("set the path for a crates.toml where lintcheck should read the sources from"), - ) - .arg( - Arg::with_name("threads") - .takes_value(true) - .value_name("N") - .short("j") - .long("jobs") - .help("number of threads to use, 0 automatic choice"), - ) - .arg( - Arg::with_name("fix") - .long("--fix") - .help("runs cargo clippy --fix and checks if all suggestions apply"), - ) - .arg( - Arg::with_name("filter") - .long("--filter") - .takes_value(true) - .multiple(true) - .value_name("clippy_lint_name") - .help("apply a filter to only collect specified lints, this also overrides `allow` attributes"), - ) - .arg( - Arg::with_name("markdown") - .long("--markdown") - .help("change the reports table to use markdown links"), - ) - .get_matches() -} - /// Returns the path to the Clippy project directory -/// -/// # Panics -/// -/// Panics if the current directory could not be retrieved, there was an error reading any of the -/// Cargo.toml files or ancestor directory is the clippy root directory #[must_use] -pub fn clippy_project_root() -> PathBuf { - let current_dir = std::env::current_dir().unwrap(); - for path in current_dir.ancestors() { - let result = std::fs::read_to_string(path.join("Cargo.toml")); - if let Err(err) = &result { - if err.kind() == std::io::ErrorKind::NotFound { - continue; - } - } - - let content = result.unwrap(); - if content.contains("[package]\nname = \"clippy\"") { - return path.to_path_buf(); - } - } - panic!("error: Can't determine root of project. Please run inside a Clippy working dir."); +fn clippy_project_root() -> &'static Path { + Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap() } #[test] diff --git a/rust-toolchain b/rust-toolchain index 03acb51306d7a..997e7ba9382b2 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-05-05" +channel = "nightly-2022-05-19" components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/main.rs b/src/main.rs index 240e233420f0e..9ee4a40cbf242 100644 --- a/src/main.rs +++ b/src/main.rs @@ -123,8 +123,12 @@ impl ClippyCmd { .map(|arg| format!("{}__CLIPPY_HACKERY__", arg)) .collect(); + // Currently, `CLIPPY_TERMINAL_WIDTH` is used only to format "unknown field" error messages. + let terminal_width = termize::dimensions().map_or(0, |(w, _)| w); + cmd.env("RUSTC_WORKSPACE_WRAPPER", Self::path()) .env("CLIPPY_ARGS", clippy_args) + .env("CLIPPY_TERMINAL_WIDTH", terminal_width.to_string()) .arg(self.cargo_subcommand) .args(&self.args); diff --git a/tests/dogfood.rs b/tests/dogfood.rs index eb97d1933d5d7..9da80518ce916 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -21,7 +21,7 @@ fn dogfood_clippy() { // "" is the root package for package in &["", "clippy_dev", "clippy_lints", "clippy_utils", "rustc_tools_util"] { - run_clippy_for_package(package); + run_clippy_for_package(package, &[]); } } @@ -38,7 +38,7 @@ fn run_metadata_collection_lint() { // Run collection as is std::env::set_var("ENABLE_METADATA_COLLECTION", "1"); - run_clippy_for_package("clippy_lints"); + run_clippy_for_package("clippy_lints", &["-A", "unfulfilled_lint_expectations"]); // Check if cargo caching got in the way if let Ok(file) = File::open(metadata_output_path) { @@ -61,10 +61,10 @@ fn run_metadata_collection_lint() { .unwrap(); // Running the collection again - run_clippy_for_package("clippy_lints"); + run_clippy_for_package("clippy_lints", &["-A", "unfulfilled_lint_expectations"]); } -fn run_clippy_for_package(project: &str) { +fn run_clippy_for_package(project: &str, args: &[&str]) { let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let mut command = Command::new(&*test_utils::CARGO_CLIPPY_PATH); @@ -76,6 +76,7 @@ fn run_clippy_for_package(project: &str) { .arg("--all-targets") .arg("--all-features") .arg("--") + .args(args) .args(&["-D", "clippy::all"]) .args(&["-D", "clippy::pedantic"]) .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir diff --git a/tests/ui-cargo/duplicate_mod/fail/Cargo.toml b/tests/ui-cargo/duplicate_mod/fail/Cargo.toml new file mode 100644 index 0000000000000..bf3c817de1c4f --- /dev/null +++ b/tests/ui-cargo/duplicate_mod/fail/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "duplicate_mod" +edition = "2021" +publish = false +version = "0.1.0" diff --git a/tests/ui-cargo/duplicate_mod/fail/src/a.rs b/tests/ui-cargo/duplicate_mod/fail/src/a.rs new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/tests/ui-cargo/duplicate_mod/fail/src/a.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/duplicate_mod/fail/src/b.rs b/tests/ui-cargo/duplicate_mod/fail/src/b.rs new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/tests/ui-cargo/duplicate_mod/fail/src/b.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/duplicate_mod/fail/src/c.rs b/tests/ui-cargo/duplicate_mod/fail/src/c.rs new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/tests/ui-cargo/duplicate_mod/fail/src/c.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/duplicate_mod/fail/src/from_other_module.rs b/tests/ui-cargo/duplicate_mod/fail/src/from_other_module.rs new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/tests/ui-cargo/duplicate_mod/fail/src/from_other_module.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/duplicate_mod/fail/src/main.rs b/tests/ui-cargo/duplicate_mod/fail/src/main.rs new file mode 100644 index 0000000000000..79b343da24700 --- /dev/null +++ b/tests/ui-cargo/duplicate_mod/fail/src/main.rs @@ -0,0 +1,16 @@ +mod a; + +mod b; +#[path = "b.rs"] +mod b2; + +mod c; +#[path = "c.rs"] +mod c2; +#[path = "c.rs"] +mod c3; + +mod from_other_module; +mod other_module; + +fn main() {} diff --git a/tests/ui-cargo/duplicate_mod/fail/src/main.stderr b/tests/ui-cargo/duplicate_mod/fail/src/main.stderr new file mode 100644 index 0000000000000..00d7739c8a2ea --- /dev/null +++ b/tests/ui-cargo/duplicate_mod/fail/src/main.stderr @@ -0,0 +1,42 @@ +error: file is loaded as a module multiple times: `$DIR/b.rs` + --> $DIR/main.rs:3:1 + | +LL | mod b; + | ^^^^^^ first loaded here +LL | / #[path = "b.rs"] +LL | | mod b2; + | |_______^ loaded again here + | + = note: `-D clippy::duplicate-mod` implied by `-D warnings` + = help: replace all but one `mod` item with `use` items + +error: file is loaded as a module multiple times: `$DIR/c.rs` + --> $DIR/main.rs:7:1 + | +LL | mod c; + | ^^^^^^ first loaded here +LL | / #[path = "c.rs"] +LL | | mod c2; + | |_______^ loaded again here +LL | / #[path = "c.rs"] +LL | | mod c3; + | |_______^ loaded again here + | + = help: replace all but one `mod` item with `use` items + +error: file is loaded as a module multiple times: `$DIR/from_other_module.rs` + --> $DIR/main.rs:13:1 + | +LL | mod from_other_module; + | ^^^^^^^^^^^^^^^^^^^^^^ first loaded here + | + ::: $DIR/other_module/mod.rs:1:1 + | +LL | / #[path = "../from_other_module.rs"] +LL | | mod m; + | |______^ loaded again here + | + = help: replace all but one `mod` item with `use` items + +error: aborting due to 3 previous errors + diff --git a/tests/ui-cargo/duplicate_mod/fail/src/other_module/mod.rs b/tests/ui-cargo/duplicate_mod/fail/src/other_module/mod.rs new file mode 100644 index 0000000000000..36ce7286aded0 --- /dev/null +++ b/tests/ui-cargo/duplicate_mod/fail/src/other_module/mod.rs @@ -0,0 +1,2 @@ +#[path = "../from_other_module.rs"] +mod m; diff --git a/tests/ui-toml/expect_used/clippy.toml b/tests/ui-toml/expect_used/clippy.toml new file mode 100644 index 0000000000000..6933b8164195a --- /dev/null +++ b/tests/ui-toml/expect_used/clippy.toml @@ -0,0 +1 @@ +allow-expect-in-tests = true diff --git a/tests/ui-toml/expect_used/expect_used.rs b/tests/ui-toml/expect_used/expect_used.rs new file mode 100644 index 0000000000000..22dcd3ae9d697 --- /dev/null +++ b/tests/ui-toml/expect_used/expect_used.rs @@ -0,0 +1,29 @@ +// compile-flags: --test +#![warn(clippy::expect_used)] + +fn expect_option() { + let opt = Some(0); + let _ = opt.expect(""); +} + +fn expect_result() { + let res: Result = Ok(0); + let _ = res.expect(""); +} + +fn main() { + expect_option(); + expect_result(); +} + +#[test] +fn test_expect_option() { + let opt = Some(0); + let _ = opt.expect(""); +} + +#[test] +fn test_expect_result() { + let res: Result = Ok(0); + let _ = res.expect(""); +} diff --git a/tests/ui-toml/expect_used/expect_used.stderr b/tests/ui-toml/expect_used/expect_used.stderr new file mode 100644 index 0000000000000..9cb2199ed21cb --- /dev/null +++ b/tests/ui-toml/expect_used/expect_used.stderr @@ -0,0 +1,19 @@ +error: used `expect()` on `an Option` value + --> $DIR/expect_used.rs:6:13 + | +LL | let _ = opt.expect(""); + | ^^^^^^^^^^^^^^ + | + = note: `-D clippy::expect-used` implied by `-D warnings` + = help: if this value is an `None`, it will panic + +error: used `expect()` on `a Result` value + --> $DIR/expect_used.rs:11:13 + | +LL | let _ = res.expect(""); + | ^^^^^^^^^^^^^^ + | + = help: if this value is an `Err`, it will panic + +error: aborting due to 2 previous errors + diff --git a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs index 338b3b5b28f42..3397fa1ec6927 100644 --- a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs +++ b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs @@ -14,4 +14,10 @@ fn main() { let _ = 2.0f32.clamp(3.0f32, 4.0f32); let _ = 2.0f64.clamp(3.0f64, 4.0f64); + + let indirect: fn(&str) -> Result = Regex::new; + let re = indirect(".").unwrap(); + + let in_call = Box::new(f32::clamp); + let in_method_call = ["^", "$"].into_iter().map(Regex::new); } diff --git a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr index 5533676aea287..5cbb567546c08 100644 --- a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr +++ b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr @@ -32,5 +32,23 @@ error: use of a disallowed method `f32::clamp` LL | let _ = 2.0f32.clamp(3.0f32, 4.0f32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 5 previous errors +error: use of a disallowed method `regex::Regex::new` + --> $DIR/conf_disallowed_methods.rs:18:61 + | +LL | let indirect: fn(&str) -> Result = Regex::new; + | ^^^^^^^^^^ + +error: use of a disallowed method `f32::clamp` + --> $DIR/conf_disallowed_methods.rs:21:28 + | +LL | let in_call = Box::new(f32::clamp); + | ^^^^^^^^^^ + +error: use of a disallowed method `regex::Regex::new` + --> $DIR/conf_disallowed_methods.rs:22:53 + | +LL | let in_method_call = ["^", "$"].into_iter().map(Regex::new); + | ^^^^^^^^^^ + +error: aborting due to 8 previous errors diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 8701809b4daaa..92838aa57dfda 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,4 +1,43 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `max-suggested-slice-pattern-length`, `await-holding-invalid-types`, `max-include-file-size`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of + allow-expect-in-tests + allow-unwrap-in-tests + allowed-scripts + array-size-threshold + avoid-breaking-exported-api + await-holding-invalid-types + blacklisted-names + cargo-ignore-publish + cognitive-complexity-threshold + cyclomatic-complexity-threshold + disallowed-methods + disallowed-types + doc-valid-idents + enable-raw-pointer-heuristic-for-send + enforced-import-renames + enum-variant-name-threshold + enum-variant-size-threshold + literal-representation-threshold + max-fn-params-bools + max-include-file-size + max-struct-bools + max-suggested-slice-pattern-length + max-trait-bounds + msrv + pass-by-value-size-limit + single-char-binding-names-threshold + standard-macro-braces + third-party + too-large-for-stack + too-many-arguments-threshold + too-many-lines-threshold + trivial-copy-size-limit + type-complexity-threshold + unreadable-literal-lint-fractions + upper-case-acronyms-aggressive + vec-box-size-threshold + verbose-bit-mask-threshold + warn-on-all-wildcard-imports + at line 5 column 1 error: aborting due to previous error diff --git a/tests/ui-toml/unwrap_used/clippy.toml b/tests/ui-toml/unwrap_used/clippy.toml new file mode 100644 index 0000000000000..154626ef4e81f --- /dev/null +++ b/tests/ui-toml/unwrap_used/clippy.toml @@ -0,0 +1 @@ +allow-unwrap-in-tests = true diff --git a/tests/ui-toml/unwrap_used/unwrap_used.rs b/tests/ui-toml/unwrap_used/unwrap_used.rs new file mode 100644 index 0000000000000..74d0d7c2650dd --- /dev/null +++ b/tests/ui-toml/unwrap_used/unwrap_used.rs @@ -0,0 +1,73 @@ +// compile-flags: --test + +#![allow(unused_mut, clippy::from_iter_instead_of_collect)] +#![warn(clippy::unwrap_used)] +#![deny(clippy::get_unwrap)] + +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::collections::VecDeque; + +struct GetFalsePositive { + arr: [u32; 3], +} + +impl GetFalsePositive { + fn get(&self, pos: usize) -> Option<&u32> { + self.arr.get(pos) + } + fn get_mut(&mut self, pos: usize) -> Option<&mut u32> { + self.arr.get_mut(pos) + } +} + +fn main() { + let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]); + let mut some_slice = &mut [0, 1, 2, 3]; + let mut some_vec = vec![0, 1, 2, 3]; + let mut some_vecdeque: VecDeque<_> = some_vec.iter().cloned().collect(); + let mut some_hashmap: HashMap = HashMap::from_iter(vec![(1, 'a'), (2, 'b')]); + let mut some_btreemap: BTreeMap = BTreeMap::from_iter(vec![(1, 'a'), (2, 'b')]); + let mut false_positive = GetFalsePositive { arr: [0, 1, 2] }; + + { + // Test `get().unwrap()` + let _ = boxed_slice.get(1).unwrap(); + let _ = some_slice.get(0).unwrap(); + let _ = some_vec.get(0).unwrap(); + let _ = some_vecdeque.get(0).unwrap(); + let _ = some_hashmap.get(&1).unwrap(); + let _ = some_btreemap.get(&1).unwrap(); + #[allow(clippy::unwrap_used)] + let _ = false_positive.get(0).unwrap(); + // Test with deref + let _: u8 = *boxed_slice.get(1).unwrap(); + } + + { + // Test `get_mut().unwrap()` + *boxed_slice.get_mut(0).unwrap() = 1; + *some_slice.get_mut(0).unwrap() = 1; + *some_vec.get_mut(0).unwrap() = 1; + *some_vecdeque.get_mut(0).unwrap() = 1; + // Check false positives + #[allow(clippy::unwrap_used)] + { + *some_hashmap.get_mut(&1).unwrap() = 'b'; + *some_btreemap.get_mut(&1).unwrap() = 'b'; + *false_positive.get_mut(0).unwrap() = 1; + } + } + + { + // Test `get().unwrap().foo()` and `get_mut().unwrap().bar()` + let _ = some_vec.get(0..1).unwrap().to_vec(); + let _ = some_vec.get_mut(0..1).unwrap().to_vec(); + } +} + +#[test] +fn test() { + let boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]); + let _ = boxed_slice.get(1).unwrap(); +} diff --git a/tests/ui-toml/unwrap_used/unwrap_used.stderr b/tests/ui-toml/unwrap_used/unwrap_used.stderr new file mode 100644 index 0000000000000..6bcfa0a8b5647 --- /dev/null +++ b/tests/ui-toml/unwrap_used/unwrap_used.stderr @@ -0,0 +1,197 @@ +error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:35:17 + | +LL | let _ = boxed_slice.get(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]` + | +note: the lint level is defined here + --> $DIR/unwrap_used.rs:5:9 + | +LL | #![deny(clippy::get_unwrap)] + | ^^^^^^^^^^^^^^^^^^ + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:35:17 + | +LL | let _ = boxed_slice.get(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unwrap-used` implied by `-D warnings` + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:36:17 + | +LL | let _ = some_slice.get(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_slice[0]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:36:17 + | +LL | let _ = some_slice.get(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:37:17 + | +LL | let _ = some_vec.get(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vec[0]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:37:17 + | +LL | let _ = some_vec.get(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:38:17 + | +LL | let _ = some_vecdeque.get(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vecdeque[0]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:38:17 + | +LL | let _ = some_vecdeque.get(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:39:17 + | +LL | let _ = some_hashmap.get(&1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_hashmap[&1]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:39:17 + | +LL | let _ = some_hashmap.get(&1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:40:17 + | +LL | let _ = some_btreemap.get(&1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_btreemap[&1]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:40:17 + | +LL | let _ = some_btreemap.get(&1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:44:21 + | +LL | let _: u8 = *boxed_slice.get(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[1]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:44:22 + | +LL | let _: u8 = *boxed_slice.get(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:49:9 + | +LL | *boxed_slice.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[0]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:49:10 + | +LL | *boxed_slice.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:50:9 + | +LL | *some_slice.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_slice[0]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:50:10 + | +LL | *some_slice.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:51:9 + | +LL | *some_vec.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:51:10 + | +LL | *some_vec.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:52:9 + | +LL | *some_vecdeque.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vecdeque[0]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:52:10 + | +LL | *some_vecdeque.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:64:17 + | +LL | let _ = some_vec.get(0..1).unwrap().to_vec(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:64:17 + | +LL | let _ = some_vec.get(0..1).unwrap().to_vec(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:65:17 + | +LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:65:17 + | +LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:72:13 + | +LL | let _ = boxed_slice.get(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]` + +error: aborting due to 27 previous errors + diff --git a/tests/ui/absurd-extreme-comparisons.rs b/tests/ui/absurd-extreme-comparisons.rs index d205b383d1ff6..f682b280c1b80 100644 --- a/tests/ui/absurd-extreme-comparisons.rs +++ b/tests/ui/absurd-extreme-comparisons.rs @@ -37,7 +37,7 @@ fn main() { use std::cmp::{Ordering, PartialEq, PartialOrd}; -#[derive(PartialEq, PartialOrd)] +#[derive(PartialEq, Eq, PartialOrd)] pub struct U(u64); impl PartialEq for U { diff --git a/tests/ui/assign_ops2.rs b/tests/ui/assign_ops2.rs index 4703a8c777788..f6d3a8fa3f0d7 100644 --- a/tests/ui/assign_ops2.rs +++ b/tests/ui/assign_ops2.rs @@ -24,7 +24,7 @@ fn main() { use std::ops::{Mul, MulAssign}; -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct Wrap(i64); impl Mul for Wrap { diff --git a/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs b/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs index 26c88489b03cb..a2ef0fe827c01 100644 --- a/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs +++ b/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs @@ -5,7 +5,6 @@ extern crate proc_macro; use proc_macro::{token_stream, Delimiter, Group, Ident, Span, TokenStream, TokenTree}; -use std::iter::FromIterator; fn read_ident(iter: &mut token_stream::IntoIter) -> Ident { match iter.next() { diff --git a/tests/ui/blacklisted_name.rs b/tests/ui/blacklisted_name.rs index 57d7139fef568..27df732a08802 100644 --- a/tests/ui/blacklisted_name.rs +++ b/tests/ui/blacklisted_name.rs @@ -46,10 +46,10 @@ fn issue_1647_ref_mut() { mod tests { fn issue_7305() { - // `blackisted_name` lint should not be triggered inside of the test code. + // `blacklisted_name` lint should not be triggered inside of the test code. let foo = 0; - // Check that even in nested functions warning is still not triggere. + // Check that even in nested functions warning is still not triggered. fn nested() { let foo = 0; } diff --git a/tests/ui/branches_sharing_code/shared_at_top.rs b/tests/ui/branches_sharing_code/shared_at_top.rs index 51a46481399b4..bdeb0a39558db 100644 --- a/tests/ui/branches_sharing_code/shared_at_top.rs +++ b/tests/ui/branches_sharing_code/shared_at_top.rs @@ -1,4 +1,4 @@ -#![allow(dead_code, clippy::eval_order_dependence)] +#![allow(dead_code, clippy::mixed_read_write_in_expression)] #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] // This tests the branches_sharing_code lint at the start of blocks diff --git a/tests/ui/branches_sharing_code/valid_if_blocks.rs b/tests/ui/branches_sharing_code/valid_if_blocks.rs index 0c70e3748ec16..a26141be23733 100644 --- a/tests/ui/branches_sharing_code/valid_if_blocks.rs +++ b/tests/ui/branches_sharing_code/valid_if_blocks.rs @@ -1,4 +1,4 @@ -#![allow(dead_code, clippy::eval_order_dependence)] +#![allow(dead_code, clippy::mixed_read_write_in_expression)] #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] // This tests valid if blocks that shouldn't trigger the lint diff --git a/tests/ui/cast_size_32bit.stderr b/tests/ui/cast_size_32bit.stderr index 140676a5ffcfc..7125f741c150e 100644 --- a/tests/ui/cast_size_32bit.stderr +++ b/tests/ui/cast_size_32bit.stderr @@ -14,26 +14,12 @@ LL | x0 as f64; | = note: `-D clippy::cast-precision-loss` implied by `-D warnings` -error: casting `isize` to `f64` may become silently lossy if you later change the type - --> $DIR/cast_size_32bit.rs:15:5 - | -LL | x0 as f64; - | ^^^^^^^^^ help: try: `f64::from(x0)` - | - = note: `-D clippy::cast-lossless` implied by `-D warnings` - error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) --> $DIR/cast_size_32bit.rs:16:5 | LL | x1 as f64; | ^^^^^^^^^ -error: casting `usize` to `f64` may become silently lossy if you later change the type - --> $DIR/cast_size_32bit.rs:16:5 - | -LL | x1 as f64; - | ^^^^^^^^^ help: try: `f64::from(x1)` - error: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide) --> $DIR/cast_size_32bit.rs:17:5 | diff --git a/tests/ui/checked_conversions.fixed b/tests/ui/checked_conversions.fixed index 12290db3dcf52..0983d393b560e 100644 --- a/tests/ui/checked_conversions.fixed +++ b/tests/ui/checked_conversions.fixed @@ -7,8 +7,6 @@ )] #![warn(clippy::checked_conversions)] -use std::convert::TryFrom; - // Positive tests // Signed to unsigned diff --git a/tests/ui/checked_conversions.rs b/tests/ui/checked_conversions.rs index 895a301e82126..7d26ace47fdf5 100644 --- a/tests/ui/checked_conversions.rs +++ b/tests/ui/checked_conversions.rs @@ -7,8 +7,6 @@ )] #![warn(clippy::checked_conversions)] -use std::convert::TryFrom; - // Positive tests // Signed to unsigned diff --git a/tests/ui/checked_conversions.stderr b/tests/ui/checked_conversions.stderr index 18518def0acbe..2e518040561c4 100644 --- a/tests/ui/checked_conversions.stderr +++ b/tests/ui/checked_conversions.stderr @@ -1,5 +1,5 @@ error: checked cast can be simplified - --> $DIR/checked_conversions.rs:17:13 + --> $DIR/checked_conversions.rs:15:13 | LL | let _ = value <= (u32::max_value() as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` @@ -7,91 +7,91 @@ LL | let _ = value <= (u32::max_value() as i64) && value >= 0; = note: `-D clippy::checked-conversions` implied by `-D warnings` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:18:13 + --> $DIR/checked_conversions.rs:16:13 | LL | let _ = value <= (u32::MAX as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:22:13 + --> $DIR/checked_conversions.rs:20:13 | LL | let _ = value <= i64::from(u16::max_value()) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:23:13 + --> $DIR/checked_conversions.rs:21:13 | LL | let _ = value <= i64::from(u16::MAX) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:27:13 + --> $DIR/checked_conversions.rs:25:13 | LL | let _ = value <= (u8::max_value() as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:28:13 + --> $DIR/checked_conversions.rs:26:13 | LL | let _ = value <= (u8::MAX as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:34:13 + --> $DIR/checked_conversions.rs:32:13 | LL | let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:35:13 + --> $DIR/checked_conversions.rs:33:13 | LL | let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:39:13 + --> $DIR/checked_conversions.rs:37:13 | LL | let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:40:13 + --> $DIR/checked_conversions.rs:38:13 | LL | let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:46:13 + --> $DIR/checked_conversions.rs:44:13 | LL | let _ = value <= i32::max_value() as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:47:13 + --> $DIR/checked_conversions.rs:45:13 | LL | let _ = value <= i32::MAX as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:51:13 + --> $DIR/checked_conversions.rs:49:13 | LL | let _ = value <= isize::max_value() as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:52:13 + --> $DIR/checked_conversions.rs:50:13 | LL | let _ = value <= isize::MAX as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:56:13 + --> $DIR/checked_conversions.rs:54:13 | LL | let _ = value <= u16::max_value() as u32 && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:57:13 + --> $DIR/checked_conversions.rs:55:13 | LL | let _ = value <= u16::MAX as u32 && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` diff --git a/tests/ui/cmp_owned/asymmetric_partial_eq.fixed b/tests/ui/cmp_owned/asymmetric_partial_eq.fixed index 3305ac9bf8b6c..abd059c230801 100644 --- a/tests/ui/cmp_owned/asymmetric_partial_eq.fixed +++ b/tests/ui/cmp_owned/asymmetric_partial_eq.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![allow(unused, clippy::redundant_clone)] // See #5700 +#![allow(unused, clippy::redundant_clone, clippy::derive_partial_eq_without_eq)] // See #5700 // Define the types in each module to avoid trait impls leaking between modules. macro_rules! impl_types { diff --git a/tests/ui/cmp_owned/asymmetric_partial_eq.rs b/tests/ui/cmp_owned/asymmetric_partial_eq.rs index 88bc2f51dd662..020ef5f840bd5 100644 --- a/tests/ui/cmp_owned/asymmetric_partial_eq.rs +++ b/tests/ui/cmp_owned/asymmetric_partial_eq.rs @@ -1,5 +1,5 @@ // run-rustfix -#![allow(unused, clippy::redundant_clone)] // See #5700 +#![allow(unused, clippy::redundant_clone, clippy::derive_partial_eq_without_eq)] // See #5700 // Define the types in each module to avoid trait impls leaking between modules. macro_rules! impl_types { diff --git a/tests/ui/cmp_owned/with_suggestion.fixed b/tests/ui/cmp_owned/with_suggestion.fixed index 05fb96339e33e..b28c4378e33c6 100644 --- a/tests/ui/cmp_owned/with_suggestion.fixed +++ b/tests/ui/cmp_owned/with_suggestion.fixed @@ -45,7 +45,7 @@ impl ToOwned for Foo { } } -#[derive(PartialEq)] +#[derive(PartialEq, Eq)] struct Bar; impl PartialEq for Bar { @@ -61,7 +61,7 @@ impl std::borrow::Borrow for Bar { } } -#[derive(PartialEq)] +#[derive(PartialEq, Eq)] struct Baz; impl ToOwned for Baz { diff --git a/tests/ui/cmp_owned/with_suggestion.rs b/tests/ui/cmp_owned/with_suggestion.rs index 0a02825ed82f1..c1089010fe109 100644 --- a/tests/ui/cmp_owned/with_suggestion.rs +++ b/tests/ui/cmp_owned/with_suggestion.rs @@ -45,7 +45,7 @@ impl ToOwned for Foo { } } -#[derive(PartialEq)] +#[derive(PartialEq, Eq)] struct Bar; impl PartialEq for Bar { @@ -61,7 +61,7 @@ impl std::borrow::Borrow for Bar { } } -#[derive(PartialEq)] +#[derive(PartialEq, Eq)] struct Baz; impl ToOwned for Baz { diff --git a/tests/ui/cmp_owned/without_suggestion.rs b/tests/ui/cmp_owned/without_suggestion.rs index f44a3901fb487..d8a202cb6a1c6 100644 --- a/tests/ui/cmp_owned/without_suggestion.rs +++ b/tests/ui/cmp_owned/without_suggestion.rs @@ -9,6 +9,10 @@ fn main() { let x = &&Baz; let y = &Baz; y.to_owned() == **x; + + let x = 0u32; + let y = U32Wrapper(x); + let _ = U32Wrapper::from(x) == y; } struct Foo; @@ -26,7 +30,7 @@ impl ToOwned for Foo { } } -#[derive(PartialEq)] +#[derive(PartialEq, Eq)] struct Baz; impl ToOwned for Baz { @@ -36,7 +40,7 @@ impl ToOwned for Baz { } } -#[derive(PartialEq)] +#[derive(PartialEq, Eq)] struct Bar; impl PartialEq for Bar { @@ -51,3 +55,21 @@ impl std::borrow::Borrow for Bar { &FOO } } + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +struct U32Wrapper(u32); +impl From for U32Wrapper { + fn from(x: u32) -> Self { + Self(x) + } +} +impl PartialEq for U32Wrapper { + fn eq(&self, other: &u32) -> bool { + self.0 == *other + } +} +impl PartialEq for u32 { + fn eq(&self, other: &U32Wrapper) -> bool { + *self == other.0 + } +} diff --git a/tests/ui/cmp_owned/without_suggestion.stderr b/tests/ui/cmp_owned/without_suggestion.stderr index 2ea3d8fac0d1a..d2dd14d8edbb8 100644 --- a/tests/ui/cmp_owned/without_suggestion.stderr +++ b/tests/ui/cmp_owned/without_suggestion.stderr @@ -13,7 +13,7 @@ LL | y.to_owned() == **x; | ^^^^^^^^^^^^^^^^^^^ try implementing the comparison without allocating error: this creates an owned instance just for comparison - --> $DIR/without_suggestion.rs:18:9 + --> $DIR/without_suggestion.rs:22:9 | LL | self.to_owned() == *other | ^^^^^^^^^^^^^^^^^^^^^^^^^ try implementing the comparison without allocating diff --git a/tests/ui/crashes/auxiliary/proc_macro_crash.rs b/tests/ui/crashes/auxiliary/proc_macro_crash.rs index ed8e7a708a5e2..5ff2af7cd8253 100644 --- a/tests/ui/crashes/auxiliary/proc_macro_crash.rs +++ b/tests/ui/crashes/auxiliary/proc_macro_crash.rs @@ -12,7 +12,6 @@ extern crate proc_macro; use proc_macro::{Delimiter, Group, Ident, Span, TokenStream, TokenTree}; -use std::iter::FromIterator; #[proc_macro] pub fn macro_test(input_stream: TokenStream) -> TokenStream { diff --git a/tests/ui/crashes/ice-6254.rs b/tests/ui/crashes/ice-6254.rs index c19eca43884aa..a2a60a1691534 100644 --- a/tests/ui/crashes/ice-6254.rs +++ b/tests/ui/crashes/ice-6254.rs @@ -2,6 +2,7 @@ // panicked at 'assertion failed: rows.iter().all(|r| r.len() == v.len())', // compiler/rustc_mir_build/src/thir/pattern/_match.rs:2030:5 +#[allow(clippy::derive_partial_eq_without_eq)] #[derive(PartialEq)] struct Foo(i32); const FOO_REF_REF: &&Foo = &&Foo(42); diff --git a/tests/ui/crashes/ice-6254.stderr b/tests/ui/crashes/ice-6254.stderr index 95ebf23d81817..f37ab2e9b0c79 100644 --- a/tests/ui/crashes/ice-6254.stderr +++ b/tests/ui/crashes/ice-6254.stderr @@ -1,5 +1,5 @@ error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/ice-6254.rs:12:9 + --> $DIR/ice-6254.rs:13:9 | LL | FOO_REF_REF => {}, | ^^^^^^^^^^^ diff --git a/tests/ui/crashes/ice-8821.rs b/tests/ui/crashes/ice-8821.rs new file mode 100644 index 0000000000000..fb87b79aeed87 --- /dev/null +++ b/tests/ui/crashes/ice-8821.rs @@ -0,0 +1,8 @@ +#![warn(clippy::let_unit_value)] + +fn f() {} +static FN: fn() = f; + +fn main() { + let _: () = FN(); +} diff --git a/tests/ui/crashes/ice-8821.stderr b/tests/ui/crashes/ice-8821.stderr new file mode 100644 index 0000000000000..486096e0a06dd --- /dev/null +++ b/tests/ui/crashes/ice-8821.stderr @@ -0,0 +1,10 @@ +error: this let-binding has unit value + --> $DIR/ice-8821.rs:7:5 + | +LL | let _: () = FN(); + | ^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `FN();` + | + = note: `-D clippy::let-unit-value` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/dbg_macro.rs b/tests/ui/dbg_macro.rs index baf01174b6764..25294e8c766f8 100644 --- a/tests/ui/dbg_macro.rs +++ b/tests/ui/dbg_macro.rs @@ -46,3 +46,15 @@ mod issue7274 { pub fn issue8481() { dbg!(1); } + +#[cfg(test)] +fn foo2() { + dbg!(1); +} + +#[cfg(test)] +mod mod1 { + fn func() { + dbg!(1); + } +} diff --git a/tests/ui/derive_hash_xor_eq.rs b/tests/ui/derive_hash_xor_eq.rs index 10abe22d53e5c..813ddc5664642 100644 --- a/tests/ui/derive_hash_xor_eq.rs +++ b/tests/ui/derive_hash_xor_eq.rs @@ -1,3 +1,5 @@ +#![allow(clippy::derive_partial_eq_without_eq)] + #[derive(PartialEq, Hash)] struct Foo; diff --git a/tests/ui/derive_hash_xor_eq.stderr b/tests/ui/derive_hash_xor_eq.stderr index b383072ca4db7..e5184bd1407c0 100644 --- a/tests/ui/derive_hash_xor_eq.stderr +++ b/tests/ui/derive_hash_xor_eq.stderr @@ -1,12 +1,12 @@ error: you are deriving `Hash` but have implemented `PartialEq` explicitly - --> $DIR/derive_hash_xor_eq.rs:10:10 + --> $DIR/derive_hash_xor_eq.rs:12:10 | LL | #[derive(Hash)] | ^^^^ | = note: `#[deny(clippy::derive_hash_xor_eq)]` on by default note: `PartialEq` implemented here - --> $DIR/derive_hash_xor_eq.rs:13:1 + --> $DIR/derive_hash_xor_eq.rs:15:1 | LL | / impl PartialEq for Bar { LL | | fn eq(&self, _: &Bar) -> bool { @@ -17,13 +17,13 @@ LL | | } = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) error: you are deriving `Hash` but have implemented `PartialEq` explicitly - --> $DIR/derive_hash_xor_eq.rs:19:10 + --> $DIR/derive_hash_xor_eq.rs:21:10 | LL | #[derive(Hash)] | ^^^^ | note: `PartialEq` implemented here - --> $DIR/derive_hash_xor_eq.rs:22:1 + --> $DIR/derive_hash_xor_eq.rs:24:1 | LL | / impl PartialEq for Baz { LL | | fn eq(&self, _: &Baz) -> bool { @@ -34,7 +34,7 @@ LL | | } = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) error: you are implementing `Hash` explicitly but have derived `PartialEq` - --> $DIR/derive_hash_xor_eq.rs:31:1 + --> $DIR/derive_hash_xor_eq.rs:33:1 | LL | / impl std::hash::Hash for Bah { LL | | fn hash(&self, _: &mut H) {} @@ -42,14 +42,14 @@ LL | | } | |_^ | note: `PartialEq` implemented here - --> $DIR/derive_hash_xor_eq.rs:28:10 + --> $DIR/derive_hash_xor_eq.rs:30:10 | LL | #[derive(PartialEq)] | ^^^^^^^^^ = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) error: you are implementing `Hash` explicitly but have derived `PartialEq` - --> $DIR/derive_hash_xor_eq.rs:49:5 + --> $DIR/derive_hash_xor_eq.rs:51:5 | LL | / impl Hash for Foo3 { LL | | fn hash(&self, _: &mut H) {} @@ -57,7 +57,7 @@ LL | | } | |_____^ | note: `PartialEq` implemented here - --> $DIR/derive_hash_xor_eq.rs:46:14 + --> $DIR/derive_hash_xor_eq.rs:48:14 | LL | #[derive(PartialEq)] | ^^^^^^^^^ diff --git a/tests/ui/derive_partial_eq_without_eq.fixed b/tests/ui/derive_partial_eq_without_eq.fixed new file mode 100644 index 0000000000000..7d4d1b3b64906 --- /dev/null +++ b/tests/ui/derive_partial_eq_without_eq.fixed @@ -0,0 +1,98 @@ +// run-rustfix + +#![allow(unused)] +#![warn(clippy::derive_partial_eq_without_eq)] + +// Don't warn on structs that aren't PartialEq +struct NotPartialEq { + foo: u32, + bar: String, +} + +// Eq can be derived but is missing +#[derive(Debug, PartialEq, Eq)] +struct MissingEq { + foo: u32, + bar: String, +} + +// Eq is derived +#[derive(PartialEq, Eq)] +struct NotMissingEq { + foo: u32, + bar: String, +} + +// Eq is manually implemented +#[derive(PartialEq)] +struct ManualEqImpl { + foo: u32, + bar: String, +} + +impl Eq for ManualEqImpl {} + +// Cannot be Eq because f32 isn't Eq +#[derive(PartialEq)] +struct CannotBeEq { + foo: u32, + bar: f32, +} + +// Don't warn if PartialEq is manually implemented +struct ManualPartialEqImpl { + foo: u32, + bar: String, +} + +impl PartialEq for ManualPartialEqImpl { + fn eq(&self, other: &Self) -> bool { + self.foo == other.foo && self.bar == other.bar + } +} + +// Generic fields should be properly checked for Eq-ness +#[derive(PartialEq)] +struct GenericNotEq { + foo: T, + bar: U, +} + +#[derive(PartialEq, Eq)] +struct GenericEq { + foo: T, + bar: U, +} + +#[derive(PartialEq, Eq)] +struct TupleStruct(u32); + +#[derive(PartialEq, Eq)] +struct GenericTupleStruct(T); + +#[derive(PartialEq)] +struct TupleStructNotEq(f32); + +#[derive(PartialEq, Eq)] +enum Enum { + Foo(u32), + Bar { a: String, b: () }, +} + +#[derive(PartialEq, Eq)] +enum GenericEnum { + Foo(T), + Bar { a: U, b: V }, +} + +#[derive(PartialEq)] +enum EnumNotEq { + Foo(u32), + Bar { a: String, b: f32 }, +} + +// Ensure that rustfix works properly when `PartialEq` has other derives on either side +#[derive(Debug, PartialEq, Eq, Clone)] +struct RustFixWithOtherDerives; + +fn main() {} diff --git a/tests/ui/derive_partial_eq_without_eq.rs b/tests/ui/derive_partial_eq_without_eq.rs new file mode 100644 index 0000000000000..ab4e1df1ca408 --- /dev/null +++ b/tests/ui/derive_partial_eq_without_eq.rs @@ -0,0 +1,98 @@ +// run-rustfix + +#![allow(unused)] +#![warn(clippy::derive_partial_eq_without_eq)] + +// Don't warn on structs that aren't PartialEq +struct NotPartialEq { + foo: u32, + bar: String, +} + +// Eq can be derived but is missing +#[derive(Debug, PartialEq)] +struct MissingEq { + foo: u32, + bar: String, +} + +// Eq is derived +#[derive(PartialEq, Eq)] +struct NotMissingEq { + foo: u32, + bar: String, +} + +// Eq is manually implemented +#[derive(PartialEq)] +struct ManualEqImpl { + foo: u32, + bar: String, +} + +impl Eq for ManualEqImpl {} + +// Cannot be Eq because f32 isn't Eq +#[derive(PartialEq)] +struct CannotBeEq { + foo: u32, + bar: f32, +} + +// Don't warn if PartialEq is manually implemented +struct ManualPartialEqImpl { + foo: u32, + bar: String, +} + +impl PartialEq for ManualPartialEqImpl { + fn eq(&self, other: &Self) -> bool { + self.foo == other.foo && self.bar == other.bar + } +} + +// Generic fields should be properly checked for Eq-ness +#[derive(PartialEq)] +struct GenericNotEq { + foo: T, + bar: U, +} + +#[derive(PartialEq)] +struct GenericEq { + foo: T, + bar: U, +} + +#[derive(PartialEq)] +struct TupleStruct(u32); + +#[derive(PartialEq)] +struct GenericTupleStruct(T); + +#[derive(PartialEq)] +struct TupleStructNotEq(f32); + +#[derive(PartialEq)] +enum Enum { + Foo(u32), + Bar { a: String, b: () }, +} + +#[derive(PartialEq)] +enum GenericEnum { + Foo(T), + Bar { a: U, b: V }, +} + +#[derive(PartialEq)] +enum EnumNotEq { + Foo(u32), + Bar { a: String, b: f32 }, +} + +// Ensure that rustfix works properly when `PartialEq` has other derives on either side +#[derive(Debug, PartialEq, Clone)] +struct RustFixWithOtherDerives; + +fn main() {} diff --git a/tests/ui/derive_partial_eq_without_eq.stderr b/tests/ui/derive_partial_eq_without_eq.stderr new file mode 100644 index 0000000000000..bf55165890a5a --- /dev/null +++ b/tests/ui/derive_partial_eq_without_eq.stderr @@ -0,0 +1,46 @@ +error: you are deriving `PartialEq` and can implement `Eq` + --> $DIR/derive_partial_eq_without_eq.rs:13:17 + | +LL | #[derive(Debug, PartialEq)] + | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` + | + = note: `-D clippy::derive-partial-eq-without-eq` implied by `-D warnings` + +error: you are deriving `PartialEq` and can implement `Eq` + --> $DIR/derive_partial_eq_without_eq.rs:61:10 + | +LL | #[derive(PartialEq)] + | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` + +error: you are deriving `PartialEq` and can implement `Eq` + --> $DIR/derive_partial_eq_without_eq.rs:67:10 + | +LL | #[derive(PartialEq)] + | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` + +error: you are deriving `PartialEq` and can implement `Eq` + --> $DIR/derive_partial_eq_without_eq.rs:70:10 + | +LL | #[derive(PartialEq)] + | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` + +error: you are deriving `PartialEq` and can implement `Eq` + --> $DIR/derive_partial_eq_without_eq.rs:76:10 + | +LL | #[derive(PartialEq)] + | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` + +error: you are deriving `PartialEq` and can implement `Eq` + --> $DIR/derive_partial_eq_without_eq.rs:82:10 + | +LL | #[derive(PartialEq)] + | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` + +error: you are deriving `PartialEq` and can implement `Eq` + --> $DIR/derive_partial_eq_without_eq.rs:95:17 + | +LL | #[derive(Debug, PartialEq, Clone)] + | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` + +error: aborting due to 7 previous errors + diff --git a/tests/ui/equatable_if_let.fixed b/tests/ui/equatable_if_let.fixed index 88918d9671e42..47bf25e409bd2 100644 --- a/tests/ui/equatable_if_let.fixed +++ b/tests/ui/equatable_if_let.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused_variables, dead_code)] +#![allow(unused_variables, dead_code, clippy::derive_partial_eq_without_eq)] #![warn(clippy::equatable_if_let)] use std::cmp::Ordering; diff --git a/tests/ui/equatable_if_let.rs b/tests/ui/equatable_if_let.rs index 9a7ab75ef450f..d498bca2455bd 100644 --- a/tests/ui/equatable_if_let.rs +++ b/tests/ui/equatable_if_let.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused_variables, dead_code)] +#![allow(unused_variables, dead_code, clippy::derive_partial_eq_without_eq)] #![warn(clippy::equatable_if_let)] use std::cmp::Ordering; diff --git a/tests/ui/from_iter_instead_of_collect.fixed b/tests/ui/from_iter_instead_of_collect.fixed index 12db43b534361..403c3b3e44380 100644 --- a/tests/ui/from_iter_instead_of_collect.fixed +++ b/tests/ui/from_iter_instead_of_collect.fixed @@ -4,7 +4,6 @@ #![allow(unused_imports)] use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque}; -use std::iter::FromIterator; struct Foo(Vec); diff --git a/tests/ui/from_iter_instead_of_collect.rs b/tests/ui/from_iter_instead_of_collect.rs index f5ec190e0cdc5..fefc7b01a65bb 100644 --- a/tests/ui/from_iter_instead_of_collect.rs +++ b/tests/ui/from_iter_instead_of_collect.rs @@ -4,7 +4,6 @@ #![allow(unused_imports)] use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque}; -use std::iter::FromIterator; struct Foo(Vec); diff --git a/tests/ui/from_iter_instead_of_collect.stderr b/tests/ui/from_iter_instead_of_collect.stderr index 8f08ac8c3ff43..8aa3c3c01f818 100644 --- a/tests/ui/from_iter_instead_of_collect.stderr +++ b/tests/ui/from_iter_instead_of_collect.stderr @@ -1,5 +1,5 @@ error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:19:9 + --> $DIR/from_iter_instead_of_collect.rs:18:9 | LL | >::from_iter(iter.into_iter().copied()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter.into_iter().copied().collect::()` @@ -7,85 +7,85 @@ LL | >::from_iter(iter.into_iter().copied()) = note: `-D clippy::from-iter-instead-of-collect` implied by `-D warnings` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:25:13 + --> $DIR/from_iter_instead_of_collect.rs:24:13 | LL | let _ = Vec::from_iter(iter_expr); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter_expr.collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:27:13 + --> $DIR/from_iter_instead_of_collect.rs:26:13 | LL | let _ = HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `vec![5, 5, 5, 5].iter().enumerate().collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:32:19 + --> $DIR/from_iter_instead_of_collect.rs:31:19 | LL | assert_eq!(a, Vec::from_iter(0..3)); | ^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:33:19 + --> $DIR/from_iter_instead_of_collect.rs:32:19 | LL | assert_eq!(a, Vec::::from_iter(0..3)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:35:17 + --> $DIR/from_iter_instead_of_collect.rs:34:17 | LL | let mut b = VecDeque::from_iter(0..3); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:38:17 + --> $DIR/from_iter_instead_of_collect.rs:37:17 | LL | let mut b = VecDeque::::from_iter(0..3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:43:21 + --> $DIR/from_iter_instead_of_collect.rs:42:21 | LL | let mut b = collections::VecDeque::::from_iter(0..3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:48:14 + --> $DIR/from_iter_instead_of_collect.rs:47:14 | LL | let bm = BTreeMap::from_iter(values.iter().cloned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `values.iter().cloned().collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:49:19 + --> $DIR/from_iter_instead_of_collect.rs:48:19 | LL | let mut bar = BTreeMap::from_iter(bm.range(0..2)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `bm.range(0..2).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:52:19 + --> $DIR/from_iter_instead_of_collect.rs:51:19 | LL | let mut bts = BTreeSet::from_iter(0..3); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:56:17 + --> $DIR/from_iter_instead_of_collect.rs:55:17 | LL | let _ = collections::BTreeSet::from_iter(0..3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:57:17 + --> $DIR/from_iter_instead_of_collect.rs:56:17 | LL | let _ = collections::BTreeSet::::from_iter(0..3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:60:15 + --> $DIR/from_iter_instead_of_collect.rs:59:15 | LL | for _i in Vec::from_iter([1, 2, 3].iter()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `[1, 2, 3].iter().collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:61:15 + --> $DIR/from_iter_instead_of_collect.rs:60:15 | LL | for _i in Vec::<&i32>::from_iter([1, 2, 3].iter()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `[1, 2, 3].iter().collect::>()` diff --git a/tests/ui/get_unwrap.fixed b/tests/ui/get_unwrap.fixed index c3a36dcabd1a9..8f165d675890c 100644 --- a/tests/ui/get_unwrap.fixed +++ b/tests/ui/get_unwrap.fixed @@ -7,7 +7,6 @@ use std::collections::BTreeMap; use std::collections::HashMap; use std::collections::VecDeque; -use std::iter::FromIterator; struct GetFalsePositive { arr: [u32; 3], diff --git a/tests/ui/get_unwrap.rs b/tests/ui/get_unwrap.rs index d77a202aa39c3..786749daa746e 100644 --- a/tests/ui/get_unwrap.rs +++ b/tests/ui/get_unwrap.rs @@ -7,7 +7,6 @@ use std::collections::BTreeMap; use std::collections::HashMap; use std::collections::VecDeque; -use std::iter::FromIterator; struct GetFalsePositive { arr: [u32; 3], diff --git a/tests/ui/get_unwrap.stderr b/tests/ui/get_unwrap.stderr index cb5f44fbd59ee..ea8fec5273511 100644 --- a/tests/ui/get_unwrap.stderr +++ b/tests/ui/get_unwrap.stderr @@ -1,5 +1,5 @@ error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:36:17 + --> $DIR/get_unwrap.rs:35:17 | LL | let _ = boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]` @@ -11,7 +11,7 @@ LL | #![deny(clippy::get_unwrap)] | ^^^^^^^^^^^^^^^^^^ error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:36:17 + --> $DIR/get_unwrap.rs:35:17 | LL | let _ = boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,13 +20,13 @@ LL | let _ = boxed_slice.get(1).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:37:17 + --> $DIR/get_unwrap.rs:36:17 | LL | let _ = some_slice.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_slice[0]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:37:17 + --> $DIR/get_unwrap.rs:36:17 | LL | let _ = some_slice.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -34,13 +34,13 @@ LL | let _ = some_slice.get(0).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:38:17 + --> $DIR/get_unwrap.rs:37:17 | LL | let _ = some_vec.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vec[0]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:38:17 + --> $DIR/get_unwrap.rs:37:17 | LL | let _ = some_vec.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -48,13 +48,13 @@ LL | let _ = some_vec.get(0).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:39:17 + --> $DIR/get_unwrap.rs:38:17 | LL | let _ = some_vecdeque.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vecdeque[0]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:39:17 + --> $DIR/get_unwrap.rs:38:17 | LL | let _ = some_vecdeque.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,13 +62,13 @@ LL | let _ = some_vecdeque.get(0).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:40:17 + --> $DIR/get_unwrap.rs:39:17 | LL | let _ = some_hashmap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_hashmap[&1]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:40:17 + --> $DIR/get_unwrap.rs:39:17 | LL | let _ = some_hashmap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -76,13 +76,13 @@ LL | let _ = some_hashmap.get(&1).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:41:17 + --> $DIR/get_unwrap.rs:40:17 | LL | let _ = some_btreemap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_btreemap[&1]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:41:17 + --> $DIR/get_unwrap.rs:40:17 | LL | let _ = some_btreemap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -90,13 +90,13 @@ LL | let _ = some_btreemap.get(&1).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:45:21 + --> $DIR/get_unwrap.rs:44:21 | LL | let _: u8 = *boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[1]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:45:22 + --> $DIR/get_unwrap.rs:44:22 | LL | let _: u8 = *boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -104,13 +104,13 @@ LL | let _: u8 = *boxed_slice.get(1).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:50:9 + --> $DIR/get_unwrap.rs:49:9 | LL | *boxed_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[0]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:50:10 + --> $DIR/get_unwrap.rs:49:10 | LL | *boxed_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -118,13 +118,13 @@ LL | *boxed_slice.get_mut(0).unwrap() = 1; = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:51:9 + --> $DIR/get_unwrap.rs:50:9 | LL | *some_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_slice[0]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:51:10 + --> $DIR/get_unwrap.rs:50:10 | LL | *some_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -132,13 +132,13 @@ LL | *some_slice.get_mut(0).unwrap() = 1; = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:52:9 + --> $DIR/get_unwrap.rs:51:9 | LL | *some_vec.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:52:10 + --> $DIR/get_unwrap.rs:51:10 | LL | *some_vec.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -146,13 +146,13 @@ LL | *some_vec.get_mut(0).unwrap() = 1; = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:53:9 + --> $DIR/get_unwrap.rs:52:9 | LL | *some_vecdeque.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vecdeque[0]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:53:10 + --> $DIR/get_unwrap.rs:52:10 | LL | *some_vecdeque.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -160,13 +160,13 @@ LL | *some_vecdeque.get_mut(0).unwrap() = 1; = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:65:17 + --> $DIR/get_unwrap.rs:64:17 | LL | let _ = some_vec.get(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:65:17 + --> $DIR/get_unwrap.rs:64:17 | LL | let _ = some_vec.get(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -174,13 +174,13 @@ LL | let _ = some_vec.get(0..1).unwrap().to_vec(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:66:17 + --> $DIR/get_unwrap.rs:65:17 | LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:66:17 + --> $DIR/get_unwrap.rs:65:17 | LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/infinite_iter.rs b/tests/ui/infinite_iter.rs index 1fe688977659d..a1e5fad0c621f 100644 --- a/tests/ui/infinite_iter.rs +++ b/tests/ui/infinite_iter.rs @@ -51,7 +51,6 @@ fn main() { mod finite_collect { use std::collections::HashSet; - use std::iter::FromIterator; struct C; impl FromIterator for C { diff --git a/tests/ui/infinite_iter.stderr b/tests/ui/infinite_iter.stderr index 5f5e7ac9f253a..ba277e36339ad 100644 --- a/tests/ui/infinite_iter.stderr +++ b/tests/ui/infinite_iter.stderr @@ -98,7 +98,7 @@ LL | (0..).all(|x| x == 24); // maybe infinite iter | ^^^^^^^^^^^^^^^^^^^^^^ error: infinite iteration detected - --> $DIR/infinite_iter.rs:64:31 + --> $DIR/infinite_iter.rs:63:31 | LL | let _: HashSet = (0..).collect(); // Infinite iter | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/manual_str_repeat.fixed b/tests/ui/manual_str_repeat.fixed index dc140257f3210..0704ba2f933eb 100644 --- a/tests/ui/manual_str_repeat.fixed +++ b/tests/ui/manual_str_repeat.fixed @@ -4,7 +4,7 @@ #![warn(clippy::manual_str_repeat)] use std::borrow::Cow; -use std::iter::{repeat, FromIterator}; +use std::iter::repeat; fn main() { let _: String = "test".repeat(10); diff --git a/tests/ui/manual_str_repeat.rs b/tests/ui/manual_str_repeat.rs index 0d69c989b2ed8..f522be439aa0e 100644 --- a/tests/ui/manual_str_repeat.rs +++ b/tests/ui/manual_str_repeat.rs @@ -4,7 +4,7 @@ #![warn(clippy::manual_str_repeat)] use std::borrow::Cow; -use std::iter::{repeat, FromIterator}; +use std::iter::repeat; fn main() { let _: String = std::iter::repeat("test").take(10).collect(); diff --git a/tests/ui/map_err.rs b/tests/ui/map_err.rs index 00e037843f8c2..bb35ab1a14efa 100644 --- a/tests/ui/map_err.rs +++ b/tests/ui/map_err.rs @@ -1,6 +1,5 @@ #![warn(clippy::map_err_ignore)] #![allow(clippy::unnecessary_wraps)] -use std::convert::TryFrom; use std::error::Error; use std::fmt; diff --git a/tests/ui/map_err.stderr b/tests/ui/map_err.stderr index 37e87e64de28f..c035840521e49 100644 --- a/tests/ui/map_err.stderr +++ b/tests/ui/map_err.stderr @@ -1,5 +1,5 @@ error: `map_err(|_|...` wildcard pattern discards the original error - --> $DIR/map_err.rs:23:32 + --> $DIR/map_err.rs:22:32 | LL | println!("{:?}", x.map_err(|_| Errors::Ignored)); | ^^^ diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index b8dc8179f7d7d..de46e6cff55ba 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -111,3 +111,16 @@ fn main() { let x = 1; println!("Not an array index start"); } + +#[allow(dead_code)] +fn issue_8723() { + let (mut val, idx) = ("a b", 1); + + let (pre, suf) = val.split_at(idx); + val = { + println!("{}", pre); + suf + }; + + let _ = val; +} diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index fe63dcd63f2bb..eea64fcb292b8 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -126,3 +126,17 @@ fn main() { _ => println!("Not an array index start"), } } + +#[allow(dead_code)] +fn issue_8723() { + let (mut val, idx) = ("a b", 1); + + val = match val.split_at(idx) { + (pre, suf) => { + println!("{}", pre); + suf + }, + }; + + let _ = val; +} diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index d939291f53c40..5d4e7314b2137 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -9,7 +9,7 @@ LL | | } | |_____^ | = note: `-D clippy::match-single-binding` implied by `-D warnings` -help: consider using `let` statement +help: consider using a `let` statement | LL ~ let (x, y, z) = (a, b, c); LL + { @@ -25,7 +25,7 @@ LL | | (x, y, z) => println!("{} {} {}", x, y, z), LL | | } | |_____^ | -help: consider using `let` statement +help: consider using a `let` statement | LL ~ let (x, y, z) = (a, b, c); LL + println!("{} {} {}", x, y, z); @@ -88,7 +88,7 @@ LL | | Point { x, y } => println!("Coords: ({}, {})", x, y), LL | | } | |_____^ | -help: consider using `let` statement +help: consider using a `let` statement | LL ~ let Point { x, y } = p; LL + println!("Coords: ({}, {})", x, y); @@ -102,7 +102,7 @@ LL | | Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1), LL | | } | |_____^ | -help: consider using `let` statement +help: consider using a `let` statement | LL ~ let Point { x: x1, y: y1 } = p; LL + println!("Coords: ({}, {})", x1, y1); @@ -116,7 +116,7 @@ LL | | ref r => println!("Got a reference to {}", r), LL | | } | |_____^ | -help: consider using `let` statement +help: consider using a `let` statement | LL ~ let ref r = x; LL + println!("Got a reference to {}", r); @@ -130,7 +130,7 @@ LL | | ref mut mr => println!("Got a mutable reference to {}", mr), LL | | } | |_____^ | -help: consider using `let` statement +help: consider using a `let` statement | LL ~ let ref mut mr = x; LL + println!("Got a mutable reference to {}", mr); @@ -144,7 +144,7 @@ LL | | Point { x, y } => x * y, LL | | }; | |______^ | -help: consider using `let` statement +help: consider using a `let` statement | LL ~ let Point { x, y } = coords(); LL + let product = x * y; @@ -159,7 +159,7 @@ LL | | unwrapped => unwrapped, LL | | }) | |_________^ | -help: consider using `let` statement +help: consider using a `let` statement | LL ~ .map(|i| { LL + let unwrapped = i.unwrap(); @@ -176,5 +176,25 @@ LL | | _ => println!("Not an array index start"), LL | | } | |_____^ help: consider using the match body instead: `println!("Not an array index start");` -error: aborting due to 12 previous errors +error: this assignment could be simplified + --> $DIR/match_single_binding.rs:134:5 + | +LL | / val = match val.split_at(idx) { +LL | | (pre, suf) => { +LL | | println!("{}", pre); +LL | | suf +LL | | }, +LL | | }; + | |_____^ + | +help: consider removing the `match` expression + | +LL ~ let (pre, suf) = val.split_at(idx); +LL + val = { +LL + println!("{}", pre); +LL + suf +LL ~ }; + | + +error: aborting due to 13 previous errors diff --git a/tests/ui/match_single_binding2.stderr b/tests/ui/match_single_binding2.stderr index d349331946601..22bf7d8be4a29 100644 --- a/tests/ui/match_single_binding2.stderr +++ b/tests/ui/match_single_binding2.stderr @@ -8,7 +8,7 @@ LL | | }, | |_____________^ | = note: `-D clippy::match-single-binding` implied by `-D warnings` -help: consider using `let` statement +help: consider using a `let` statement | LL ~ Some((iter, _item)) => { LL + let (min, max) = iter.size_hint(); @@ -24,7 +24,7 @@ LL | | (a, b) => println!("a {:?} and b {:?}", a, b), LL | | } | |_____________^ | -help: consider using `let` statement +help: consider using a `let` statement | LL ~ let (a, b) = get_tup(); LL + println!("a {:?} and b {:?}", a, b); diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index 977ce54327b3d..9805097084d30 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -26,7 +26,6 @@ use std::collections::BTreeMap; use std::collections::HashMap; use std::collections::HashSet; use std::collections::VecDeque; -use std::iter::FromIterator; use std::ops::Mul; use std::rc::{self, Rc}; use std::sync::{self, Arc}; diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index b63672dd6fdbb..6be38b24fbda2 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -1,5 +1,5 @@ error: methods called `new` usually return `Self` - --> $DIR/methods.rs:104:5 + --> $DIR/methods.rs:103:5 | LL | / fn new() -> i32 { LL | | 0 @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::new-ret-no-self` implied by `-D warnings` error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead - --> $DIR/methods.rs:125:13 + --> $DIR/methods.rs:124:13 | LL | let _ = v.iter().filter(|&x| { | _____________^ diff --git a/tests/ui/eval_order_dependence.rs b/tests/ui/mixed_read_write_in_expression.rs similarity index 97% rename from tests/ui/eval_order_dependence.rs rename to tests/ui/mixed_read_write_in_expression.rs index aad78319d4820..7640057ab6e36 100644 --- a/tests/ui/eval_order_dependence.rs +++ b/tests/ui/mixed_read_write_in_expression.rs @@ -1,4 +1,4 @@ -#[warn(clippy::eval_order_dependence)] +#[warn(clippy::mixed_read_write_in_expression)] #[allow( unused_assignments, unused_variables, diff --git a/tests/ui/eval_order_dependence.stderr b/tests/ui/mixed_read_write_in_expression.stderr similarity index 63% rename from tests/ui/eval_order_dependence.stderr rename to tests/ui/mixed_read_write_in_expression.stderr index 7c6265a08790d..2e951cdbcbfd7 100644 --- a/tests/ui/eval_order_dependence.stderr +++ b/tests/ui/mixed_read_write_in_expression.stderr @@ -1,48 +1,48 @@ error: unsequenced read of `x` - --> $DIR/eval_order_dependence.rs:14:9 + --> $DIR/mixed_read_write_in_expression.rs:14:9 | LL | } + x; | ^ | - = note: `-D clippy::eval-order-dependence` implied by `-D warnings` + = note: `-D clippy::mixed-read-write-in-expression` implied by `-D warnings` note: whether read occurs before this write depends on evaluation order - --> $DIR/eval_order_dependence.rs:12:9 + --> $DIR/mixed_read_write_in_expression.rs:12:9 | LL | x = 1; | ^^^^^ error: unsequenced read of `x` - --> $DIR/eval_order_dependence.rs:17:5 + --> $DIR/mixed_read_write_in_expression.rs:17:5 | LL | x += { | ^ | note: whether read occurs before this write depends on evaluation order - --> $DIR/eval_order_dependence.rs:18:9 + --> $DIR/mixed_read_write_in_expression.rs:18:9 | LL | x = 20; | ^^^^^^ error: unsequenced read of `x` - --> $DIR/eval_order_dependence.rs:30:12 + --> $DIR/mixed_read_write_in_expression.rs:30:12 | LL | a: x, | ^ | note: whether read occurs before this write depends on evaluation order - --> $DIR/eval_order_dependence.rs:32:13 + --> $DIR/mixed_read_write_in_expression.rs:32:13 | LL | x = 6; | ^^^^^ error: unsequenced read of `x` - --> $DIR/eval_order_dependence.rs:39:9 + --> $DIR/mixed_read_write_in_expression.rs:39:9 | LL | x += { | ^ | note: whether read occurs before this write depends on evaluation order - --> $DIR/eval_order_dependence.rs:40:13 + --> $DIR/mixed_read_write_in_expression.rs:40:13 | LL | x = 20; | ^^^^^^ diff --git a/tests/ui/range_contains.fixed b/tests/ui/range_contains.fixed index 47c974e614b91..f497719971174 100644 --- a/tests/ui/range_contains.fixed +++ b/tests/ui/range_contains.fixed @@ -6,7 +6,7 @@ #[allow(clippy::short_circuit_statement)] #[allow(clippy::unnecessary_operation)] fn main() { - let x = 9_u32; + let x = 9_i32; // order shouldn't matter (8..12).contains(&x); @@ -43,6 +43,12 @@ fn main() { let y = 3.; (0. ..1.).contains(&y); !(0. ..=1.).contains(&y); + + // handle negatives #8721 + (-10..=10).contains(&x); + x >= 10 && x <= -10; + (-3. ..=3.).contains(&y); + y >= 3. && y <= -3.; } // Fix #6373 diff --git a/tests/ui/range_contains.rs b/tests/ui/range_contains.rs index 835deced5e4cb..9e2180b0c9944 100644 --- a/tests/ui/range_contains.rs +++ b/tests/ui/range_contains.rs @@ -6,7 +6,7 @@ #[allow(clippy::short_circuit_statement)] #[allow(clippy::unnecessary_operation)] fn main() { - let x = 9_u32; + let x = 9_i32; // order shouldn't matter x >= 8 && x < 12; @@ -43,6 +43,12 @@ fn main() { let y = 3.; y >= 0. && y < 1.; y < 0. || y > 1.; + + // handle negatives #8721 + x >= -10 && x <= 10; + x >= 10 && x <= -10; + y >= -3. && y <= 3.; + y >= 3. && y <= -3.; } // Fix #6373 diff --git a/tests/ui/range_contains.stderr b/tests/ui/range_contains.stderr index bc79f1bca8463..1817ee1715d17 100644 --- a/tests/ui/range_contains.stderr +++ b/tests/ui/range_contains.stderr @@ -84,5 +84,17 @@ error: manual `!RangeInclusive::contains` implementation LL | y < 0. || y > 1.; | ^^^^^^^^^^^^^^^^ help: use: `!(0. ..=1.).contains(&y)` -error: aborting due to 14 previous errors +error: manual `RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:48:5 + | +LL | x >= -10 && x <= 10; + | ^^^^^^^^^^^^^^^^^^^ help: use: `(-10..=10).contains(&x)` + +error: manual `RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:50:5 + | +LL | y >= -3. && y <= 3.; + | ^^^^^^^^^^^^^^^^^^^ help: use: `(-3. ..=3.).contains(&y)` + +error: aborting due to 16 previous errors diff --git a/tests/ui/rc_clone_in_vec_init/arc.rs b/tests/ui/rc_clone_in_vec_init/arc.rs new file mode 100644 index 0000000000000..384060e6eae50 --- /dev/null +++ b/tests/ui/rc_clone_in_vec_init/arc.rs @@ -0,0 +1,68 @@ +#![warn(clippy::rc_clone_in_vec_init)] +use std::sync::{Arc, Mutex}; + +fn main() {} + +fn should_warn_simple_case() { + let v = vec![Arc::new("x".to_string()); 2]; +} + +fn should_warn_simple_case_with_big_indentation() { + if true { + let k = 1; + dbg!(k); + if true { + let v = vec![Arc::new("x".to_string()); 2]; + } + } +} + +fn should_warn_complex_case() { + let v = vec![ + std::sync::Arc::new(Mutex::new({ + let x = 1; + dbg!(x); + x + })); + 2 + ]; + + let v1 = vec![ + Arc::new(Mutex::new({ + let x = 1; + dbg!(x); + x + })); + 2 + ]; +} + +fn should_not_warn_custom_arc() { + #[derive(Clone)] + struct Arc; + + impl Arc { + fn new() -> Self { + Arc + } + } + + let v = vec![Arc::new(); 2]; +} + +fn should_not_warn_vec_from_elem_but_not_arc() { + let v = vec![String::new(); 2]; + let v1 = vec![1; 2]; + let v2 = vec![ + Box::new(std::sync::Arc::new({ + let y = 3; + dbg!(y); + y + })); + 2 + ]; +} + +fn should_not_warn_vec_macro_but_not_from_elem() { + let v = vec![Arc::new("x".to_string())]; +} diff --git a/tests/ui/rc_clone_in_vec_init/arc.stderr b/tests/ui/rc_clone_in_vec_init/arc.stderr new file mode 100644 index 0000000000000..ce84186c8e300 --- /dev/null +++ b/tests/ui/rc_clone_in_vec_init/arc.stderr @@ -0,0 +1,109 @@ +error: calling `Arc::new` in `vec![elem; len]` + --> $DIR/arc.rs:7:13 + | +LL | let v = vec![Arc::new("x".to_string()); 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::rc-clone-in-vec-init` implied by `-D warnings` + = note: each element will point to the same `Arc` instance +help: consider initializing each `Arc` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Arc::new("x".to_string()))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Arc` initialization to a variable + | +LL ~ let v = { +LL + let data = Arc::new("x".to_string()); +LL + vec![data; 2] +LL ~ }; + | + +error: calling `Arc::new` in `vec![elem; len]` + --> $DIR/arc.rs:15:21 + | +LL | let v = vec![Arc::new("x".to_string()); 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: each element will point to the same `Arc` instance +help: consider initializing each `Arc` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Arc::new("x".to_string()))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Arc` initialization to a variable + | +LL ~ let v = { +LL + let data = Arc::new("x".to_string()); +LL + vec![data; 2] +LL ~ }; + | + +error: calling `Arc::new` in `vec![elem; len]` + --> $DIR/arc.rs:21:13 + | +LL | let v = vec![ + | _____________^ +LL | | std::sync::Arc::new(Mutex::new({ +LL | | let x = 1; +LL | | dbg!(x); +... | +LL | | 2 +LL | | ]; + | |_____^ + | + = note: each element will point to the same `Arc` instance +help: consider initializing each `Arc` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(std::sync::Arc::new(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Arc` initialization to a variable + | +LL ~ let v = { +LL + let data = std::sync::Arc::new(..); +LL + vec![data; 2] +LL ~ }; + | + +error: calling `Arc::new` in `vec![elem; len]` + --> $DIR/arc.rs:30:14 + | +LL | let v1 = vec![ + | ______________^ +LL | | Arc::new(Mutex::new({ +LL | | let x = 1; +LL | | dbg!(x); +... | +LL | | 2 +LL | | ]; + | |_____^ + | + = note: each element will point to the same `Arc` instance +help: consider initializing each `Arc` element individually + | +LL ~ let v1 = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Arc::new(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Arc` initialization to a variable + | +LL ~ let v1 = { +LL + let data = Arc::new(..); +LL + vec![data; 2] +LL ~ }; + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/rc_clone_in_vec_init/rc.rs b/tests/ui/rc_clone_in_vec_init/rc.rs new file mode 100644 index 0000000000000..0394457fe1708 --- /dev/null +++ b/tests/ui/rc_clone_in_vec_init/rc.rs @@ -0,0 +1,69 @@ +#![warn(clippy::rc_clone_in_vec_init)] +use std::rc::Rc; +use std::sync::Mutex; + +fn main() {} + +fn should_warn_simple_case() { + let v = vec![Rc::new("x".to_string()); 2]; +} + +fn should_warn_simple_case_with_big_indentation() { + if true { + let k = 1; + dbg!(k); + if true { + let v = vec![Rc::new("x".to_string()); 2]; + } + } +} + +fn should_warn_complex_case() { + let v = vec![ + std::rc::Rc::new(Mutex::new({ + let x = 1; + dbg!(x); + x + })); + 2 + ]; + + let v1 = vec![ + Rc::new(Mutex::new({ + let x = 1; + dbg!(x); + x + })); + 2 + ]; +} + +fn should_not_warn_custom_arc() { + #[derive(Clone)] + struct Rc; + + impl Rc { + fn new() -> Self { + Rc + } + } + + let v = vec![Rc::new(); 2]; +} + +fn should_not_warn_vec_from_elem_but_not_rc() { + let v = vec![String::new(); 2]; + let v1 = vec![1; 2]; + let v2 = vec![ + Box::new(std::rc::Rc::new({ + let y = 3; + dbg!(y); + y + })); + 2 + ]; +} + +fn should_not_warn_vec_macro_but_not_from_elem() { + let v = vec![Rc::new("x".to_string())]; +} diff --git a/tests/ui/rc_clone_in_vec_init/rc.stderr b/tests/ui/rc_clone_in_vec_init/rc.stderr new file mode 100644 index 0000000000000..0f5cc0cf98fea --- /dev/null +++ b/tests/ui/rc_clone_in_vec_init/rc.stderr @@ -0,0 +1,109 @@ +error: calling `Rc::new` in `vec![elem; len]` + --> $DIR/rc.rs:8:13 + | +LL | let v = vec![Rc::new("x".to_string()); 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::rc-clone-in-vec-init` implied by `-D warnings` + = note: each element will point to the same `Rc` instance +help: consider initializing each `Rc` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Rc::new("x".to_string()))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Rc` initialization to a variable + | +LL ~ let v = { +LL + let data = Rc::new("x".to_string()); +LL + vec![data; 2] +LL ~ }; + | + +error: calling `Rc::new` in `vec![elem; len]` + --> $DIR/rc.rs:16:21 + | +LL | let v = vec![Rc::new("x".to_string()); 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: each element will point to the same `Rc` instance +help: consider initializing each `Rc` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Rc::new("x".to_string()))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Rc` initialization to a variable + | +LL ~ let v = { +LL + let data = Rc::new("x".to_string()); +LL + vec![data; 2] +LL ~ }; + | + +error: calling `Rc::new` in `vec![elem; len]` + --> $DIR/rc.rs:22:13 + | +LL | let v = vec![ + | _____________^ +LL | | std::rc::Rc::new(Mutex::new({ +LL | | let x = 1; +LL | | dbg!(x); +... | +LL | | 2 +LL | | ]; + | |_____^ + | + = note: each element will point to the same `Rc` instance +help: consider initializing each `Rc` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(std::rc::Rc::new(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Rc` initialization to a variable + | +LL ~ let v = { +LL + let data = std::rc::Rc::new(..); +LL + vec![data; 2] +LL ~ }; + | + +error: calling `Rc::new` in `vec![elem; len]` + --> $DIR/rc.rs:31:14 + | +LL | let v1 = vec![ + | ______________^ +LL | | Rc::new(Mutex::new({ +LL | | let x = 1; +LL | | dbg!(x); +... | +LL | | 2 +LL | | ]; + | |_____^ + | + = note: each element will point to the same `Rc` instance +help: consider initializing each `Rc` element individually + | +LL ~ let v1 = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Rc::new(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Rc` initialization to a variable + | +LL ~ let v1 = { +LL + let data = Rc::new(..); +LL + vec![data; 2] +LL ~ }; + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/recursive_format_impl.stderr b/tests/ui/recursive_format_impl.stderr index 6171696ed69d4..1a717ac92d8b7 100644 --- a/tests/ui/recursive_format_impl.stderr +++ b/tests/ui/recursive_format_impl.stderr @@ -6,15 +6,6 @@ LL | write!(f, "{}", self.to_string()) | = note: `-D clippy::recursive-format-impl` implied by `-D warnings` -error: unnecessary use of `to_string` - --> $DIR/recursive_format_impl.rs:61:50 - | -LL | Self::E(string) => write!(f, "E {}", string.to_string()), - | ^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::unnecessary-to-owned` implied by `-D warnings` - = note: this error originates in the macro `$crate::format_args` (in Nightly builds, run with -Z macro-backtrace for more info) - error: using `self` as `Display` in `impl Display` will cause infinite recursion --> $DIR/recursive_format_impl.rs:73:9 | @@ -87,5 +78,5 @@ LL | write!(f, "{}", &&**&&*self) | = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 11 previous errors +error: aborting due to 10 previous errors diff --git a/tests/ui/redundant_allocation.rs b/tests/ui/redundant_allocation.rs index 80f94e5f3cbbb..cf7d8c6e349af 100644 --- a/tests/ui/redundant_allocation.rs +++ b/tests/ui/redundant_allocation.rs @@ -97,4 +97,39 @@ mod box_dyn { pub fn test_rc_box(_: Rc>>) {} } +// https://github.com/rust-lang/rust-clippy/issues/8604 +mod box_fat_ptr { + use std::boxed::Box; + use std::path::Path; + use std::rc::Rc; + use std::sync::Arc; + + pub struct DynSized { + foo: [usize], + } + + struct S { + a: Box>, + b: Rc>, + c: Arc>, + + e: Box>, + f: Box>, + g: Box>, + } + + pub fn test_box_str(_: Box>) {} + pub fn test_rc_str(_: Rc>) {} + pub fn test_arc_str(_: Arc>) {} + + pub fn test_box_slice(_: Box>) {} + pub fn test_box_path(_: Box>) {} + pub fn test_box_custom(_: Box>) {} + + pub fn test_rc_box_str(_: Rc>>) {} + pub fn test_rc_box_slice(_: Rc>>) {} + pub fn test_rc_box_path(_: Rc>>) {} + pub fn test_rc_box_custom(_: Rc>>) {} +} + fn main() {} diff --git a/tests/ui/redundant_allocation.stderr b/tests/ui/redundant_allocation.stderr index c3b10e5f5e679..fab1b069fcbc7 100644 --- a/tests/ui/redundant_allocation.stderr +++ b/tests/ui/redundant_allocation.stderr @@ -143,5 +143,41 @@ LL | pub fn test_rc_box(_: Rc>>) {} = note: `Box>` is already on the heap, `Rc>>` makes an extra allocation = help: consider using just `Rc>` or `Box>` -error: aborting due to 16 previous errors +error: usage of `Rc>>` + --> $DIR/redundant_allocation.rs:129:31 + | +LL | pub fn test_rc_box_str(_: Rc>>) {} + | ^^^^^^^^^^^^^^^^^ + | + = note: `Box>` is already on the heap, `Rc>>` makes an extra allocation + = help: consider using just `Rc>` or `Box>` + +error: usage of `Rc>>` + --> $DIR/redundant_allocation.rs:130:33 + | +LL | pub fn test_rc_box_slice(_: Rc>>) {} + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `Box>` is already on the heap, `Rc>>` makes an extra allocation + = help: consider using just `Rc>` or `Box>` + +error: usage of `Rc>>` + --> $DIR/redundant_allocation.rs:131:32 + | +LL | pub fn test_rc_box_path(_: Rc>>) {} + | ^^^^^^^^^^^^^^^^^^ + | + = note: `Box>` is already on the heap, `Rc>>` makes an extra allocation + = help: consider using just `Rc>` or `Box>` + +error: usage of `Rc>>` + --> $DIR/redundant_allocation.rs:132:34 + | +LL | pub fn test_rc_box_custom(_: Rc>>) {} + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `Box>` is already on the heap, `Rc>>` makes an extra allocation + = help: consider using just `Rc>` or `Box>` + +error: aborting due to 20 previous errors diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index 9c4079ad6d306..53288be9404c2 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -10,6 +10,7 @@ #![allow(clippy::cognitive_complexity)] #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] +#![allow(clippy::mixed_read_write_in_expression)] #![allow(clippy::for_loops_over_fallibles)] #![allow(clippy::useless_conversion)] #![allow(clippy::match_result_ok)] @@ -39,6 +40,7 @@ #![warn(clippy::cognitive_complexity)] #![warn(clippy::disallowed_methods)] #![warn(clippy::disallowed_types)] +#![warn(clippy::mixed_read_write_in_expression)] #![warn(clippy::for_loops_over_fallibles)] #![warn(clippy::for_loops_over_fallibles)] #![warn(clippy::useless_conversion)] diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index e83e66b7fbd42..539f34f847acd 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -10,6 +10,7 @@ #![allow(clippy::cognitive_complexity)] #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] +#![allow(clippy::mixed_read_write_in_expression)] #![allow(clippy::for_loops_over_fallibles)] #![allow(clippy::useless_conversion)] #![allow(clippy::match_result_ok)] @@ -39,6 +40,7 @@ #![warn(clippy::cyclomatic_complexity)] #![warn(clippy::disallowed_method)] #![warn(clippy::disallowed_type)] +#![warn(clippy::eval_order_dependence)] #![warn(clippy::for_loop_over_option)] #![warn(clippy::for_loop_over_result)] #![warn(clippy::identity_conversion)] diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index f811b10d01710..8ea46b580a8c9 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:35:9 + --> $DIR/rename.rs:36:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` @@ -7,202 +7,208 @@ LL | #![warn(clippy::block_in_if_condition_expr)] = note: `-D renamed-and-removed-lints` implied by `-D warnings` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:36:9 + --> $DIR/rename.rs:37:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> $DIR/rename.rs:37:9 + --> $DIR/rename.rs:38:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> $DIR/rename.rs:38:9 + --> $DIR/rename.rs:39:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> $DIR/rename.rs:39:9 + --> $DIR/rename.rs:40:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> $DIR/rename.rs:40:9 + --> $DIR/rename.rs:41:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> $DIR/rename.rs:41:9 + --> $DIR/rename.rs:42:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` +error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` + --> $DIR/rename.rs:43:9 + | +LL | #![warn(clippy::eval_order_dependence)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` + error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles` - --> $DIR/rename.rs:42:9 + --> $DIR/rename.rs:44:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `clippy::for_loops_over_fallibles` - --> $DIR/rename.rs:43:9 + --> $DIR/rename.rs:45:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> $DIR/rename.rs:44:9 + --> $DIR/rename.rs:46:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> $DIR/rename.rs:45:9 + --> $DIR/rename.rs:47:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> $DIR/rename.rs:46:9 + --> $DIR/rename.rs:48:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> $DIR/rename.rs:47:9 + --> $DIR/rename.rs:49:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:48:9 + --> $DIR/rename.rs:50:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:49:9 + --> $DIR/rename.rs:51:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:50:9 + --> $DIR/rename.rs:52:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:51:9 + --> $DIR/rename.rs:53:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> $DIR/rename.rs:52:9 + --> $DIR/rename.rs:54:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:53:9 + --> $DIR/rename.rs:55:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:54:9 + --> $DIR/rename.rs:56:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:55:9 + --> $DIR/rename.rs:57:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> $DIR/rename.rs:56:9 + --> $DIR/rename.rs:58:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> $DIR/rename.rs:57:9 + --> $DIR/rename.rs:59:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> $DIR/rename.rs:58:9 + --> $DIR/rename.rs:60:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> $DIR/rename.rs:59:9 + --> $DIR/rename.rs:61:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> $DIR/rename.rs:60:9 + --> $DIR/rename.rs:62:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> $DIR/rename.rs:61:9 + --> $DIR/rename.rs:63:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> $DIR/rename.rs:62:9 + --> $DIR/rename.rs:64:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> $DIR/rename.rs:63:9 + --> $DIR/rename.rs:65:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> $DIR/rename.rs:64:9 + --> $DIR/rename.rs:66:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> $DIR/rename.rs:65:9 + --> $DIR/rename.rs:67:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` - --> $DIR/rename.rs:66:9 + --> $DIR/rename.rs:68:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> $DIR/rename.rs:67:9 + --> $DIR/rename.rs:69:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> $DIR/rename.rs:68:9 + --> $DIR/rename.rs:70:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` -error: aborting due to 34 previous errors +error: aborting due to 35 previous errors diff --git a/tests/ui/self_assignment.rs b/tests/ui/self_assignment.rs index a7cbb9cd78b15..ef64762291024 100644 --- a/tests/ui/self_assignment.rs +++ b/tests/ui/self_assignment.rs @@ -39,7 +39,7 @@ pub fn negatives_not_equal(mut a: usize, b: &mut usize, mut s: S) { t.0 = t.1; } -#[allow(clippy::eval_order_dependence)] +#[allow(clippy::mixed_read_write_in_expression)] pub fn negatives_side_effects() { let mut v = vec![1, 2, 3, 4, 5]; let mut i = 0; diff --git a/tests/ui/significant_drop_in_scrutinee.rs b/tests/ui/significant_drop_in_scrutinee.rs index c4a3301e72265..f83a6dd0eb288 100644 --- a/tests/ui/significant_drop_in_scrutinee.rs +++ b/tests/ui/significant_drop_in_scrutinee.rs @@ -29,8 +29,8 @@ fn should_not_trigger_lint_with_mutex_guard_outside_match() { match is_foo { true => { mutex.lock().unwrap().bar(); - } - false => {} + }, + false => {}, }; } @@ -43,8 +43,8 @@ fn should_not_trigger_lint_with_mutex_guard_when_taking_ownership_in_match() { Ok(guard) => { guard.foo(); mutex.lock().unwrap().bar(); - } - _ => {} + }, + _ => {}, }; } @@ -57,8 +57,8 @@ fn should_trigger_lint_with_mutex_guard_in_match_scrutinee() { match mutex.lock().unwrap().foo() { true => { mutex.lock().unwrap().bar(); - } - false => {} + }, + false => {}, }; } @@ -68,10 +68,10 @@ fn should_not_trigger_lint_for_insignificant_drop() { match 1u64.to_string().is_empty() { true => { println!("It was empty") - } + }, false => { println!("It was not empty") - } + }, } } @@ -131,12 +131,12 @@ fn should_trigger_lint_with_wrapped_mutex() { 1 => { println!("Got 1. Is it still 1?"); println!("{}", s.lock_m().get_the_value()); - } + }, 2 => { println!("Got 2. Is it still 2?"); println!("{}", s.lock_m().get_the_value()); - } - _ => {} + }, + _ => {}, } println!("All done!"); } @@ -152,12 +152,12 @@ fn should_trigger_lint_with_double_wrapped_mutex() { 1 => { println!("Got 1. Is it still 1?"); println!("{}", s.lock_m().get_the_value()); - } + }, 2 => { println!("Got 2. Is it still 2?"); println!("{}", s.lock_m().get_the_value()); - } - _ => {} + }, + _ => {}, } println!("All done!"); } @@ -201,10 +201,10 @@ fn should_trigger_lint_for_vec() { let current_count = counter.i.load(Ordering::Relaxed); println!("Current count {}", current_count); assert_eq!(current_count, 0); - } - 1 => {} - 3 => {} - _ => {} + }, + 1 => {}, + 3 => {}, + _ => {}, }; } @@ -224,8 +224,8 @@ fn should_trigger_lint_for_tuple_in_scrutinee() { println!("started"); mutex1.lock().unwrap().s.len(); println!("done"); - } - (_, _) => {} + }, + (_, _) => {}, }; match (true, mutex1.lock().unwrap().s.len(), true) { @@ -233,8 +233,8 @@ fn should_trigger_lint_for_tuple_in_scrutinee() { println!("started"); mutex1.lock().unwrap().s.len(); println!("done"); - } - (_, _, _) => {} + }, + (_, _, _) => {}, }; let mutex2 = Mutex::new(StateWithField { s: "two".to_owned() }); @@ -244,8 +244,8 @@ fn should_trigger_lint_for_tuple_in_scrutinee() { mutex1.lock().unwrap().s.len(); mutex2.lock().unwrap().s.len(); println!("done"); - } - (_, _, _) => {} + }, + (_, _, _) => {}, }; let mutex3 = Mutex::new(StateWithField { s: "three".to_owned() }); @@ -255,19 +255,18 @@ fn should_trigger_lint_for_tuple_in_scrutinee() { mutex1.lock().unwrap().s.len(); mutex2.lock().unwrap().s.len(); println!("done"); - } - _ => {} + }, + _ => {}, }; - match (true, mutex3.lock().unwrap().s.as_str()) { (_, "three") => { println!("started"); mutex1.lock().unwrap().s.len(); mutex2.lock().unwrap().s.len(); println!("done"); - } - (_, _) => {} + }, + (_, _) => {}, }; } } @@ -282,15 +281,15 @@ fn should_trigger_lint_for_accessing_field_in_mutex_in_one_side_of_binary_op() { match mutex.lock().unwrap().s.len() > 1 { true => { mutex.lock().unwrap().s.len(); - } - false => {} + }, + false => {}, }; match 1 < mutex.lock().unwrap().s.len() { true => { mutex.lock().unwrap().s.len(); - } - false => {} + }, + false => {}, }; } @@ -300,20 +299,30 @@ fn should_trigger_lint_for_accessing_field_in_mutex_in_one_side_of_binary_op() { // drop problem, the lint recommends moving the entire binary operation. fn should_trigger_lint_for_accessing_fields_in_mutex_in_both_sides_of_binary_op() { let mutex1 = Mutex::new(StateWithField { s: "state".to_owned() }); - let mutex2 = Mutex::new(StateWithField { s: "statewithfield".to_owned() }); + let mutex2 = Mutex::new(StateWithField { + s: "statewithfield".to_owned(), + }); match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() { true => { - println!("{} < {}", mutex1.lock().unwrap().s.len(), mutex2.lock().unwrap().s.len()); - } - false => {} + println!( + "{} < {}", + mutex1.lock().unwrap().s.len(), + mutex2.lock().unwrap().s.len() + ); + }, + false => {}, }; match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() { true => { - println!("{} >= {}", mutex1.lock().unwrap().s.len(), mutex2.lock().unwrap().s.len()); - } - false => {} + println!( + "{} >= {}", + mutex1.lock().unwrap().s.len(), + mutex2.lock().unwrap().s.len() + ); + }, + false => {}, }; } @@ -328,8 +337,8 @@ fn should_not_trigger_lint_for_closure_in_scrutinee() { match get_mutex_guard() > 1 { true => { mutex1.lock().unwrap().s.len(); - } - false => {} + }, + false => {}, }; } @@ -343,8 +352,8 @@ fn should_trigger_lint_for_return_from_closure_in_scrutinee() { match get_mutex_guard().s.len() > 1 { true => { mutex1.lock().unwrap().s.len(); - } - false => {} + }, + false => {}, }; } @@ -357,13 +366,20 @@ fn should_trigger_lint_for_return_from_match_in_scrutinee() { // Should trigger lint because the nested match within the scrutinee returns a temporary with a // significant drop is but not used directly in any match arms, so it has a potentially // surprising lifetime. - match match i { 100 => mutex1.lock().unwrap(), _ => mutex2.lock().unwrap() }.s.len() > 1 { + match match i { + 100 => mutex1.lock().unwrap(), + _ => mutex2.lock().unwrap(), + } + .s + .len() + > 1 + { true => { mutex1.lock().unwrap().s.len(); - } + }, false => { println!("nothing to do here"); - } + }, }; } @@ -376,11 +392,19 @@ fn should_trigger_lint_for_return_from_if_in_scrutinee() { // Should trigger lint because the nested if-expression within the scrutinee returns a temporary // with a significant drop is but not used directly in any match arms, so it has a potentially // surprising lifetime. - match if i > 1 { mutex1.lock().unwrap() } else { mutex2.lock().unwrap() }.s.len() > 1 { + match if i > 1 { + mutex1.lock().unwrap() + } else { + mutex2.lock().unwrap() + } + .s + .len() + > 1 + { true => { mutex1.lock().unwrap().s.len(); - } - false => {} + }, + false => {}, }; } @@ -392,11 +416,15 @@ fn should_not_trigger_lint_for_if_in_scrutinee() { // Should not trigger the lint because the temporary with a significant drop *is* dropped within // the body of the if-expression nested within the match scrutinee, and therefore does not have // a potentially surprising lifetime. - match if i > 1 { mutex.lock().unwrap().s.len() > 1 } else { false } { + match if i > 1 { + mutex.lock().unwrap().s.len() > 1 + } else { + false + } { true => { mutex.lock().unwrap().s.len(); - } - false => {} + }, + false => {}, }; } @@ -430,7 +458,9 @@ struct StateStringWithBoxedMutexGuard { impl StateStringWithBoxedMutexGuard { fn new() -> StateStringWithBoxedMutexGuard { - StateStringWithBoxedMutexGuard { s: Mutex::new("A String".to_owned()) } + StateStringWithBoxedMutexGuard { + s: Mutex::new("A String".to_owned()), + } } fn lock(&self) -> Box> { Box::new(self.s.lock().unwrap()) @@ -450,7 +480,6 @@ fn should_trigger_lint_for_boxed_mutex_guard_holding_string() { }; } - struct StateWithIntField { i: u64, } @@ -467,36 +496,36 @@ fn should_trigger_lint_in_assign_expr() { match mutex.lock().unwrap().i = i { _ => { println!("{}", mutex.lock().unwrap().i); - } + }, }; match i = mutex.lock().unwrap().i { _ => { println!("{}", mutex.lock().unwrap().i); - } + }, }; match mutex.lock().unwrap().i += 1 { _ => { println!("{}", mutex.lock().unwrap().i); - } + }, }; match i += mutex.lock().unwrap().i { _ => { println!("{}", mutex.lock().unwrap().i); - } + }, }; } #[derive(Debug)] enum RecursiveEnum { - Foo(Option>) + Foo(Option>), } #[derive(Debug)] enum GenericRecursiveEnum { - Foo(T, Option>>) + Foo(T, Option>>), } fn should_not_cause_stack_overflow() { @@ -506,20 +535,20 @@ fn should_not_cause_stack_overflow() { match f { RecursiveEnum::Foo(Some(f)) => { println!("{:?}", f) - } + }, RecursiveEnum::Foo(f) => { println!("{:?}", f) - } + }, } let f = GenericRecursiveEnum::Foo(1u64, Some(Box::new(GenericRecursiveEnum::Foo(2u64, None)))); match f { GenericRecursiveEnum::Foo(i, Some(f)) => { println!("{} {:?}", i, f) - } + }, GenericRecursiveEnum::Foo(i, f) => { println!("{} {:?}", i, f) - } + }, } } diff --git a/tests/ui/significant_drop_in_scrutinee.stderr b/tests/ui/significant_drop_in_scrutinee.stderr index c442e93f539ae..af16056498505 100644 --- a/tests/ui/significant_drop_in_scrutinee.stderr +++ b/tests/ui/significant_drop_in_scrutinee.stderr @@ -102,13 +102,13 @@ LL | match mutex3.lock().unwrap().s.as_str() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:263:22 + --> $DIR/significant_drop_in_scrutinee.rs:262:22 | LL | match (true, mutex3.lock().unwrap().s.as_str()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:282:11 + --> $DIR/significant_drop_in_scrutinee.rs:281:11 | LL | match mutex.lock().unwrap().s.len() > 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -120,7 +120,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:289:11 + --> $DIR/significant_drop_in_scrutinee.rs:288:11 | LL | match 1 < mutex.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -132,7 +132,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:305:11 + --> $DIR/significant_drop_in_scrutinee.rs:306:11 | LL | match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -144,7 +144,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:312:11 + --> $DIR/significant_drop_in_scrutinee.rs:317:11 | LL | match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -156,7 +156,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:343:11 + --> $DIR/significant_drop_in_scrutinee.rs:352:11 | LL | match get_mutex_guard().s.len() > 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -168,31 +168,53 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:360:11 - | -LL | match match i { 100 => mutex1.lock().unwrap(), _ => mutex2.lock().unwrap() }.s.len() > 1 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + --> $DIR/significant_drop_in_scrutinee.rs:369:11 + | +LL | match match i { + | ___________^ +LL | | 100 => mutex1.lock().unwrap(), +LL | | _ => mutex2.lock().unwrap(), +LL | | } +LL | | .s +LL | | .len() +LL | | > 1 + | |___________^ | help: try moving the temporary above the match | -LL ~ let value = match i { 100 => mutex1.lock().unwrap(), _ => mutex2.lock().unwrap() }.s.len() > 1; -LL ~ match value { - | +LL ~ let value = match i { +LL + 100 => mutex1.lock().unwrap(), +LL + _ => mutex2.lock().unwrap(), +LL + } +LL + .s +LL + .len() + ... error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:379:11 - | -LL | match if i > 1 { mutex1.lock().unwrap() } else { mutex2.lock().unwrap() }.s.len() > 1 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + --> $DIR/significant_drop_in_scrutinee.rs:395:11 + | +LL | match if i > 1 { + | ___________^ +LL | | mutex1.lock().unwrap() +LL | | } else { +LL | | mutex2.lock().unwrap() +... | +LL | | .len() +LL | | > 1 + | |___________^ | help: try moving the temporary above the match | -LL ~ let value = if i > 1 { mutex1.lock().unwrap() } else { mutex2.lock().unwrap() }.s.len() > 1; -LL ~ match value { - | +LL ~ let value = if i > 1 { +LL + mutex1.lock().unwrap() +LL + } else { +LL + mutex2.lock().unwrap() +LL + } +LL + .s + ... error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:421:11 + --> $DIR/significant_drop_in_scrutinee.rs:449:11 | LL | match s.lock().deref().deref() { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -204,13 +226,13 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:447:11 + --> $DIR/significant_drop_in_scrutinee.rs:477:11 | LL | match s.lock().deref().deref() { | ^^^^^^^^^^^^^^^^^^^^^^^^ error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:467:11 + --> $DIR/significant_drop_in_scrutinee.rs:496:11 | LL | match mutex.lock().unwrap().i = i { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -222,7 +244,7 @@ LL ~ match () { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:473:11 + --> $DIR/significant_drop_in_scrutinee.rs:502:11 | LL | match i = mutex.lock().unwrap().i { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -234,7 +256,7 @@ LL ~ match () { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:479:11 + --> $DIR/significant_drop_in_scrutinee.rs:508:11 | LL | match mutex.lock().unwrap().i += 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -246,7 +268,7 @@ LL ~ match () { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:485:11 + --> $DIR/significant_drop_in_scrutinee.rs:514:11 | LL | match i += mutex.lock().unwrap().i { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/undocumented_unsafe_blocks.rs b/tests/ui/undocumented_unsafe_blocks.rs index 7be15b0b2dd3d..33b6a82f9d2c3 100644 --- a/tests/ui/undocumented_unsafe_blocks.rs +++ b/tests/ui/undocumented_unsafe_blocks.rs @@ -1,7 +1,7 @@ // aux-build:proc_macro_unsafe.rs #![warn(clippy::undocumented_unsafe_blocks)] -#![allow(clippy::let_unit_value)] +#![allow(clippy::let_unit_value, clippy::missing_safety_doc)] extern crate proc_macro_unsafe; @@ -334,4 +334,155 @@ pub fn print_binary_tree() { println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); } +mod unsafe_impl_smoke_test { + unsafe trait A {} + + // error: no safety comment + unsafe impl A for () {} + + // Safety: ok + unsafe impl A for (i32) {} + + mod sub_mod { + // error: + unsafe impl B for (u32) {} + unsafe trait B {} + } + + #[rustfmt::skip] + mod sub_mod2 { + // + // SAFETY: ok + // + + unsafe impl B for (u32) {} + unsafe trait B {} + } +} + +mod unsafe_impl_from_macro { + unsafe trait T {} + + // error + macro_rules! no_safety_comment { + ($t:ty) => { + unsafe impl T for $t {} + }; + } + + // ok + no_safety_comment!(()); + + // ok + macro_rules! with_safety_comment { + ($t:ty) => { + // SAFETY: + unsafe impl T for $t {} + }; + } + + // ok + with_safety_comment!((i32)); +} + +mod unsafe_impl_macro_and_not_macro { + unsafe trait T {} + + // error + macro_rules! no_safety_comment { + ($t:ty) => { + unsafe impl T for $t {} + }; + } + + // ok + no_safety_comment!(()); + + // error + unsafe impl T for (i32) {} + + // ok + no_safety_comment!(u32); + + // error + unsafe impl T for (bool) {} +} + +#[rustfmt::skip] +mod unsafe_impl_valid_comment { + unsafe trait SaFety {} + // SaFety: + unsafe impl SaFety for () {} + + unsafe trait MultiLineComment {} + // The following impl is safe + // ... + // Safety: reason + unsafe impl MultiLineComment for () {} + + unsafe trait NoAscii {} + // 安全 SAFETY: 以下のコードは安全です + unsafe impl NoAscii for () {} + + unsafe trait InlineAndPrecedingComment {} + // SAFETY: + /* comment */ unsafe impl InlineAndPrecedingComment for () {} + + unsafe trait BuriedSafety {} + // Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor + // incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation + // ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in + // reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint + // occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est + // laborum. Safety: + // Tellus elementum sagittis vitae et leo duis ut diam quam. Sit amet nulla facilisi + // morbi tempus iaculis urna. Amet luctus venenatis lectus magna. At quis risus sed vulputate odio + // ut. Luctus venenatis lectus magna fringilla urna. Tortor id aliquet lectus proin nibh nisl + // condimentum id venenatis. Vulputate dignissim suspendisse in est ante in nibh mauris cursus. + unsafe impl BuriedSafety for () {} + + unsafe trait MultiLineBlockComment {} + /* This is a description + * Safety: */ + unsafe impl MultiLineBlockComment for () {} +} + +#[rustfmt::skip] +mod unsafe_impl_invalid_comment { + unsafe trait NoComment {} + + unsafe impl NoComment for () {} + + unsafe trait InlineComment {} + + /* SAFETY: */ unsafe impl InlineComment for () {} + + unsafe trait TrailingComment {} + + unsafe impl TrailingComment for () {} // SAFETY: + + unsafe trait Interference {} + // SAFETY: + const BIG_NUMBER: i32 = 1000000; + unsafe impl Interference for () {} +} + +unsafe trait ImplInFn {} + +fn impl_in_fn() { + // error + unsafe impl ImplInFn for () {} + + // SAFETY: ok + unsafe impl ImplInFn for (i32) {} +} + +unsafe trait CrateRoot {} + +// error +unsafe impl CrateRoot for () {} + +// SAFETY: ok +unsafe impl CrateRoot for (i32) {} + fn main() {} diff --git a/tests/ui/undocumented_unsafe_blocks.stderr b/tests/ui/undocumented_unsafe_blocks.stderr index 87d445bd7b86b..b79949e9d06d6 100644 --- a/tests/ui/undocumented_unsafe_blocks.stderr +++ b/tests/ui/undocumented_unsafe_blocks.stderr @@ -147,5 +147,121 @@ LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); | = help: consider adding a safety comment on the preceding line -error: aborting due to 18 previous errors +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:341:5 + | +LL | unsafe impl A for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:348:9 + | +LL | unsafe impl B for (u32) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:369:13 + | +LL | unsafe impl T for $t {} + | ^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | no_safety_comment!(()); + | ---------------------- in this macro invocation + | + = help: consider adding a safety comment on the preceding line + = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:394:13 + | +LL | unsafe impl T for $t {} + | ^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | no_safety_comment!(()); + | ---------------------- in this macro invocation + | + = help: consider adding a safety comment on the preceding line + = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:402:5 + | +LL | unsafe impl T for (i32) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:394:13 + | +LL | unsafe impl T for $t {} + | ^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | no_safety_comment!(u32); + | ----------------------- in this macro invocation + | + = help: consider adding a safety comment on the preceding line + = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:408:5 + | +LL | unsafe impl T for (bool) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:454:5 + | +LL | unsafe impl NoComment for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:458:19 + | +LL | /* SAFETY: */ unsafe impl InlineComment for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:462:5 + | +LL | unsafe impl TrailingComment for () {} // SAFETY: + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:467:5 + | +LL | unsafe impl Interference for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:474:5 + | +LL | unsafe impl ImplInFn for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:483:1 + | +LL | unsafe impl CrateRoot for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: aborting due to 31 previous errors diff --git a/tests/ui/unit_cmp.rs b/tests/ui/unit_cmp.rs index 8d3a4eed82e3e..3d271104361b6 100644 --- a/tests/ui/unit_cmp.rs +++ b/tests/ui/unit_cmp.rs @@ -1,5 +1,9 @@ #![warn(clippy::unit_cmp)] -#![allow(clippy::no_effect, clippy::unnecessary_operation)] +#![allow( + clippy::no_effect, + clippy::unnecessary_operation, + clippy::derive_partial_eq_without_eq +)] #[derive(PartialEq)] pub struct ContainsUnit(()); // should be fine diff --git a/tests/ui/unit_cmp.stderr b/tests/ui/unit_cmp.stderr index 824506a4257ef..41cf19ae685ed 100644 --- a/tests/ui/unit_cmp.stderr +++ b/tests/ui/unit_cmp.stderr @@ -1,5 +1,5 @@ error: ==-comparison of unit values detected. This will always be true - --> $DIR/unit_cmp.rs:12:8 + --> $DIR/unit_cmp.rs:16:8 | LL | if { | ________^ @@ -12,7 +12,7 @@ LL | | } {} = note: `-D clippy::unit-cmp` implied by `-D warnings` error: >-comparison of unit values detected. This will always be false - --> $DIR/unit_cmp.rs:18:8 + --> $DIR/unit_cmp.rs:22:8 | LL | if { | ________^ @@ -23,7 +23,7 @@ LL | | } {} | |_____^ error: `assert_eq` of unit values detected. This will always succeed - --> $DIR/unit_cmp.rs:24:5 + --> $DIR/unit_cmp.rs:28:5 | LL | / assert_eq!( LL | | { @@ -35,7 +35,7 @@ LL | | ); | |_____^ error: `debug_assert_eq` of unit values detected. This will always succeed - --> $DIR/unit_cmp.rs:32:5 + --> $DIR/unit_cmp.rs:36:5 | LL | / debug_assert_eq!( LL | | { @@ -47,7 +47,7 @@ LL | | ); | |_____^ error: `assert_ne` of unit values detected. This will always fail - --> $DIR/unit_cmp.rs:41:5 + --> $DIR/unit_cmp.rs:45:5 | LL | / assert_ne!( LL | | { @@ -59,7 +59,7 @@ LL | | ); | |_____^ error: `debug_assert_ne` of unit values detected. This will always fail - --> $DIR/unit_cmp.rs:49:5 + --> $DIR/unit_cmp.rs:53:5 | LL | / debug_assert_ne!( LL | | { diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed index 7455e22d49b21..f4f76cd3dd493 100644 --- a/tests/ui/unnecessary_to_owned.fixed +++ b/tests/ui/unnecessary_to_owned.fixed @@ -78,10 +78,10 @@ fn main() { require_slice(array.as_ref()); require_slice(array_ref.as_ref()); require_slice(slice); - require_slice(x_ref); + require_slice(&x_ref.to_owned()); // No longer flagged because of #8759. require_x(&Cow::::Owned(x.clone())); - require_x(x_ref); + require_x(&x_ref.to_owned()); // No longer flagged because of #8759. require_deref_c_str(c_str); require_deref_os_str(os_str); @@ -152,6 +152,7 @@ fn main() { require_os_str(&OsString::from("x")); require_path(&std::path::PathBuf::from("x")); require_str(&String::from("x")); + require_slice(&[String::from("x")]); } fn require_c_str(_: &CStr) {} @@ -272,3 +273,59 @@ mod issue_8507 { Box::new(build(y)) } } + +// https://github.com/rust-lang/rust-clippy/issues/8759 +mod issue_8759 { + #![allow(dead_code)] + + #[derive(Default)] + struct View {} + + impl std::borrow::ToOwned for View { + type Owned = View; + fn to_owned(&self) -> Self::Owned { + View {} + } + } + + #[derive(Default)] + struct RenderWindow { + default_view: View, + } + + impl RenderWindow { + fn default_view(&self) -> &View { + &self.default_view + } + fn set_view(&mut self, _view: &View) {} + } + + fn main() { + let mut rw = RenderWindow::default(); + rw.set_view(&rw.default_view().to_owned()); + } +} + +mod issue_8759_variant { + #![allow(dead_code)] + + #[derive(Clone, Default)] + struct View {} + + #[derive(Default)] + struct RenderWindow { + default_view: View, + } + + impl RenderWindow { + fn default_view(&self) -> &View { + &self.default_view + } + fn set_view(&mut self, _view: &View) {} + } + + fn main() { + let mut rw = RenderWindow::default(); + rw.set_view(&rw.default_view().to_owned()); + } +} diff --git a/tests/ui/unnecessary_to_owned.rs b/tests/ui/unnecessary_to_owned.rs index bbcd00ad2204a..fe09a489ab0a6 100644 --- a/tests/ui/unnecessary_to_owned.rs +++ b/tests/ui/unnecessary_to_owned.rs @@ -78,10 +78,10 @@ fn main() { require_slice(&array.to_owned()); require_slice(&array_ref.to_owned()); require_slice(&slice.to_owned()); - require_slice(&x_ref.to_owned()); + require_slice(&x_ref.to_owned()); // No longer flagged because of #8759. require_x(&Cow::::Owned(x.clone()).into_owned()); - require_x(&x_ref.to_owned()); + require_x(&x_ref.to_owned()); // No longer flagged because of #8759. require_deref_c_str(c_str.to_owned()); require_deref_os_str(os_str.to_owned()); @@ -152,6 +152,7 @@ fn main() { require_os_str(&OsString::from("x").to_os_string()); require_path(&std::path::PathBuf::from("x").to_path_buf()); require_str(&String::from("x").to_string()); + require_slice(&[String::from("x")].to_owned()); } fn require_c_str(_: &CStr) {} @@ -272,3 +273,59 @@ mod issue_8507 { Box::new(build(y.to_string())) } } + +// https://github.com/rust-lang/rust-clippy/issues/8759 +mod issue_8759 { + #![allow(dead_code)] + + #[derive(Default)] + struct View {} + + impl std::borrow::ToOwned for View { + type Owned = View; + fn to_owned(&self) -> Self::Owned { + View {} + } + } + + #[derive(Default)] + struct RenderWindow { + default_view: View, + } + + impl RenderWindow { + fn default_view(&self) -> &View { + &self.default_view + } + fn set_view(&mut self, _view: &View) {} + } + + fn main() { + let mut rw = RenderWindow::default(); + rw.set_view(&rw.default_view().to_owned()); + } +} + +mod issue_8759_variant { + #![allow(dead_code)] + + #[derive(Clone, Default)] + struct View {} + + #[derive(Default)] + struct RenderWindow { + default_view: View, + } + + impl RenderWindow { + fn default_view(&self) -> &View { + &self.default_view + } + fn set_view(&mut self, _view: &View) {} + } + + fn main() { + let mut rw = RenderWindow::default(); + rw.set_view(&rw.default_view().to_owned()); + } +} diff --git a/tests/ui/unnecessary_to_owned.stderr b/tests/ui/unnecessary_to_owned.stderr index f9713559e4f7f..af7e7b41fb004 100644 --- a/tests/ui/unnecessary_to_owned.stderr +++ b/tests/ui/unnecessary_to_owned.stderr @@ -47,6 +47,18 @@ note: this value is dropped without further use LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^^^^^^ +error: redundant clone + --> $DIR/unnecessary_to_owned.rs:155:39 + | +LL | require_slice(&[String::from("x")].to_owned()); + | ^^^^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/unnecessary_to_owned.rs:155:20 + | +LL | require_slice(&[String::from("x")].to_owned()); + | ^^^^^^^^^^^^^^^^^^^ + error: unnecessary use of `into_owned` --> $DIR/unnecessary_to_owned.rs:60:36 | @@ -151,24 +163,12 @@ error: unnecessary use of `to_owned` LL | require_slice(&slice.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `slice` -error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:81:19 - | -LL | require_slice(&x_ref.to_owned()); - | ^^^^^^^^^^^^^^^^^ help: use: `x_ref` - error: unnecessary use of `into_owned` --> $DIR/unnecessary_to_owned.rs:83:42 | LL | require_x(&Cow::::Owned(x.clone()).into_owned()); | ^^^^^^^^^^^^^ help: remove this -error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:84:15 - | -LL | require_x(&x_ref.to_owned()); - | ^^^^^^^^^^^^^^^^^ help: use: `x_ref` - error: unnecessary use of `to_owned` --> $DIR/unnecessary_to_owned.rs:86:25 | @@ -476,7 +476,7 @@ LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owne | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:197:14 + --> $DIR/unnecessary_to_owned.rs:198:14 | LL | for t in file_types.to_vec() { | ^^^^^^^^^^^^^^^^^^^ @@ -492,22 +492,22 @@ LL + let path = match get_file_path(t) { | error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:220:14 + --> $DIR/unnecessary_to_owned.rs:221:14 | LL | let _ = &["x"][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().cloned()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:225:14 + --> $DIR/unnecessary_to_owned.rs:226:14 | LL | let _ = &["x"][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().copied()` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:272:24 + --> $DIR/unnecessary_to_owned.rs:273:24 | LL | Box::new(build(y.to_string())) | ^^^^^^^^^^^^^ help: use: `y` -error: aborting due to 79 previous errors +error: aborting due to 78 previous errors diff --git a/tests/ui/useless_conversion_try.rs b/tests/ui/useless_conversion_try.rs index 3787ea991445c..39f54c27bee1a 100644 --- a/tests/ui/useless_conversion_try.rs +++ b/tests/ui/useless_conversion_try.rs @@ -1,7 +1,5 @@ #![deny(clippy::useless_conversion)] -use std::convert::{TryFrom, TryInto}; - fn test_generic(val: T) -> T { let _ = T::try_from(val).unwrap(); val.try_into().unwrap() diff --git a/tests/ui/useless_conversion_try.stderr b/tests/ui/useless_conversion_try.stderr index 2e0d9129bfb30..b691c13f7dbb7 100644 --- a/tests/ui/useless_conversion_try.stderr +++ b/tests/ui/useless_conversion_try.stderr @@ -1,5 +1,5 @@ error: useless conversion to the same type: `T` - --> $DIR/useless_conversion_try.rs:6:13 + --> $DIR/useless_conversion_try.rs:4:13 | LL | let _ = T::try_from(val).unwrap(); | ^^^^^^^^^^^^^^^^ @@ -12,7 +12,7 @@ LL | #![deny(clippy::useless_conversion)] = help: consider removing `T::try_from()` error: useless conversion to the same type: `T` - --> $DIR/useless_conversion_try.rs:7:5 + --> $DIR/useless_conversion_try.rs:5:5 | LL | val.try_into().unwrap() | ^^^^^^^^^^^^^^ @@ -20,7 +20,7 @@ LL | val.try_into().unwrap() = help: consider removing `.try_into()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion_try.rs:29:21 + --> $DIR/useless_conversion_try.rs:27:21 | LL | let _: String = "foo".to_string().try_into().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -28,7 +28,7 @@ LL | let _: String = "foo".to_string().try_into().unwrap(); = help: consider removing `.try_into()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion_try.rs:30:21 + --> $DIR/useless_conversion_try.rs:28:21 | LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -36,7 +36,7 @@ LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); = help: consider removing `TryFrom::try_from()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion_try.rs:31:13 + --> $DIR/useless_conversion_try.rs:29:13 | LL | let _ = String::try_from("foo".to_string()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | let _ = String::try_from("foo".to_string()).unwrap(); = help: consider removing `String::try_from()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion_try.rs:32:13 + --> $DIR/useless_conversion_try.rs:30:13 | LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -52,7 +52,7 @@ LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); = help: consider removing `String::try_from()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion_try.rs:33:21 + --> $DIR/useless_conversion_try.rs:31:21 | LL | let _: String = format!("Hello {}", "world").try_into().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -60,7 +60,7 @@ LL | let _: String = format!("Hello {}", "world").try_into().unwrap(); = help: consider removing `.try_into()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion_try.rs:34:21 + --> $DIR/useless_conversion_try.rs:32:21 | LL | let _: String = "".to_owned().try_into().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -68,7 +68,7 @@ LL | let _: String = "".to_owned().try_into().unwrap(); = help: consider removing `.try_into()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion_try.rs:35:27 + --> $DIR/useless_conversion_try.rs:33:27 | LL | let _: String = match String::from("_").try_into() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/vec_init_then_push.rs b/tests/ui/vec_init_then_push.rs index 5099aad83bcbc..531745424a7d0 100644 --- a/tests/ui/vec_init_then_push.rs +++ b/tests/ui/vec_init_then_push.rs @@ -29,6 +29,12 @@ fn main() { // no lint vec.push(1); } + + let mut vec = Vec::with_capacity(5); + vec.push(1); + vec.push(2); + vec.push(3); + vec.push(4); } pub fn no_lint() -> Vec { @@ -44,3 +50,57 @@ pub fn no_lint() -> Vec { } } } + +fn _from_iter(items: impl Iterator) -> Vec { + let mut v = Vec::new(); + v.push(0); + v.push(1); + v.extend(items); + v +} + +fn _cond_push(x: bool) -> Vec { + let mut v = Vec::new(); + v.push(0); + if x { + v.push(1); + } + v.push(2); + v +} + +fn _push_then_edit(x: u32) -> Vec { + let mut v = Vec::new(); + v.push(x); + v.push(1); + v[0] = v[1] + 5; + v +} + +fn _cond_push_with_large_start(x: bool) -> Vec { + let mut v = Vec::new(); + v.push(0); + v.push(1); + v.push(0); + v.push(1); + v.push(0); + v.push(0); + v.push(1); + v.push(0); + if x { + v.push(1); + } + + let mut v2 = Vec::new(); + v2.push(0); + v2.push(1); + v2.push(0); + v2.push(1); + v2.push(0); + v2.push(0); + v2.push(1); + v2.push(0); + v2.extend(&v); + + v2 +} diff --git a/tests/ui/vec_init_then_push.stderr b/tests/ui/vec_init_then_push.stderr index 9ec3e10e62470..50b029fc33727 100644 --- a/tests/ui/vec_init_then_push.stderr +++ b/tests/ui/vec_init_then_push.stderr @@ -3,7 +3,7 @@ error: calls to `push` immediately after creation | LL | / let mut def_err: Vec = Default::default(); LL | | def_err.push(0); - | |____________________^ help: consider using the `vec![]` macro: `let mut def_err: Vec = vec![..];` + | |____________________^ help: consider using the `vec![]` macro: `let def_err: Vec = vec![..];` | = note: `-D clippy::vec-init-then-push` implied by `-D warnings` @@ -30,5 +30,37 @@ LL | / new_err = Vec::new(); LL | | new_err.push(0); | |____________________^ help: consider using the `vec![]` macro: `new_err = vec![..];` -error: aborting due to 4 previous errors +error: calls to `push` immediately after creation + --> $DIR/vec_init_then_push.rs:73:5 + | +LL | / let mut v = Vec::new(); +LL | | v.push(x); +LL | | v.push(1); + | |______________^ help: consider using the `vec![]` macro: `let mut v = vec![..];` + +error: calls to `push` immediately after creation + --> $DIR/vec_init_then_push.rs:81:5 + | +LL | / let mut v = Vec::new(); +LL | | v.push(0); +LL | | v.push(1); +LL | | v.push(0); +... | +LL | | v.push(1); +LL | | v.push(0); + | |______________^ help: consider using the `vec![]` macro: `let mut v = vec![..];` + +error: calls to `push` immediately after creation + --> $DIR/vec_init_then_push.rs:94:5 + | +LL | / let mut v2 = Vec::new(); +LL | | v2.push(0); +LL | | v2.push(1); +LL | | v2.push(0); +... | +LL | | v2.push(1); +LL | | v2.push(0); + | |_______________^ help: consider using the `vec![]` macro: `let mut v2 = vec![..];` + +error: aborting due to 7 previous errors diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index 97c974003c62f..2076d1299783d 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -94,6 +94,29 @@ @media (min-width: 992px) { .search-control { margin-top: 0; + float: right; + } + } + + @media (min-width: 405px) { + #upper-filters { + display: flex; + } + } + + @media (max-width: 430px) { + /* Turn the version filter list to the left */ + #version-filter-selector { + right: 0; + left: auto; + } + } + + @media (max-width: 412px) { + #upper-filters, + .panel-body .search-control { + padding-right: 8px; + padding-left: 8px; } } @@ -244,7 +267,7 @@ cursor: pointer; } - .theme-choice>li:hover { + .theme-choice > li:hover { background: var(--theme-hover); } @@ -273,23 +296,44 @@ border: 1px solid var(--theme-popup-border); } - #filter-label, #filter-clear { + #version-filter-selector .checkbox { + display: flex; + } + + #version-filter { + min-width: available; + } + + #version-filter li label { + padding-right: 0; + width: 35%; + } + + .version-filter-input { + height: 60%; + width: 30%; + text-align: center; + border: none; + border-bottom: 1px solid #000000; + } + + #filter-label, .filter-clear { background: var(--searchbar-bg); color: var(--searchbar-fg); border-color: var(--theme-popup-border); filter: brightness(95%); } - #filter-label:hover, #filter-clear:hover { + #filter-label:hover, .filter-clear:hover { filter: brightness(90%); } - #filter-input { + .filter-input { background: var(--searchbar-bg); color: var(--searchbar-fg); border-color: var(--theme-popup-border); } - #filter-input::-webkit-input-placeholder, - #filter-input::-moz-placeholder { + .filter-input::-webkit-input-placeholder, + .filter-input::-moz-placeholder { color: var(--searchbar-fg); opacity: 30%; } @@ -338,7 +382,7 @@

Clippy Lints

-
+
+
+
+ + +
+
+
-
+
- - + + - @@ -406,12 +484,15 @@

Clippy Lints

-
+

{{lint.id}} + + 📋 +
@@ -462,295 +543,6 @@

- + diff --git a/util/gh-pages/script.js b/util/gh-pages/script.js new file mode 100644 index 0000000000000..bf4ce79b2cbc9 --- /dev/null +++ b/util/gh-pages/script.js @@ -0,0 +1,371 @@ +(function () { + var md = window.markdownit({ + html: true, + linkify: true, + typographer: true, + highlight: function (str, lang) { + if (lang && hljs.getLanguage(lang)) { + try { + return '
' +
+                        hljs.highlight(lang, str, true).value +
+                        '
'; + } catch (__) {} + } + + return '
' + md.utils.escapeHtml(str) + '
'; + } + }); + + function scrollToLint(lintId) { + var target = document.getElementById(lintId); + if (!target) { + return; + } + target.scrollIntoView(); + } + + function scrollToLintByURL($scope) { + var removeListener = $scope.$on('ngRepeatFinished', function(ngRepeatFinishedEvent) { + scrollToLint(window.location.hash.slice(1)); + removeListener(); + }); + } + + function selectGroup($scope, selectedGroup) { + var groups = $scope.groups; + for (var group in groups) { + if (groups.hasOwnProperty(group)) { + if (group === selectedGroup) { + groups[group] = true; + } else { + groups[group] = false; + } + } + } + } + + angular.module("clippy", []) + .filter('markdown', function ($sce) { + return function (text) { + return $sce.trustAsHtml( + md.render(text || '') + // Oh deer, what a hack :O + .replace(' minorVersion) { return false; } + break; + default: + return true + } + } + } + + return true; + } + + $scope.byGroups = function (lint) { + return $scope.groups[lint.group]; + }; + + $scope.bySearch = function (lint, index, array) { + let searchStr = $scope.search; + // It can be `null` I haven't missed this value + if (searchStr == null || searchStr.length < 3) { + return true; + } + searchStr = searchStr.toLowerCase(); + + // Search by id + if (lint.id.indexOf(searchStr.replace("-", "_")) !== -1) { + return true; + } + + // Search the description + // The use of `for`-loops instead of `foreach` enables us to return early + let terms = searchStr.split(" "); + let docsLowerCase = lint.docs.toLowerCase(); + for (index = 0; index < terms.length; index++) { + // This is more likely and will therefor be checked first + if (docsLowerCase.indexOf(terms[index]) !== -1) { + continue; + } + + if (lint.id.indexOf(terms[index]) !== -1) { + continue; + } + + return false; + } + + return true; + } + + $scope.copyToClipboard = function (lint) { + const clipboard = document.getElementById("clipboard-" + lint.id); + if (clipboard) { + let resetClipboardTimeout = null; + let resetClipboardIcon = clipboard.innerHTML; + + function resetClipboard() { + resetClipboardTimeout = null; + clipboard.innerHTML = resetClipboardIcon; + } + + navigator.clipboard.writeText("clippy::" + lint.id); + + clipboard.innerHTML = "✓"; + if (resetClipboardTimeout !== null) { + clearTimeout(resetClipboardTimeout); + } + resetClipboardTimeout = setTimeout(resetClipboard, 1000); + } + } + + // Get data + $scope.open = {}; + $scope.loading = true; + // This will be used to jump into the source code of the version that this documentation is for. + $scope.docVersion = window.location.pathname.split('/')[2] || "master"; + + if (window.location.hash.length > 1) { + $scope.search = window.location.hash.slice(1); + $scope.open[window.location.hash.slice(1)] = true; + scrollToLintByURL($scope); + } + + $http.get('./lints.json') + .success(function (data) { + $scope.data = data; + $scope.loading = false; + + var selectedGroup = getQueryVariable("sel"); + if (selectedGroup) { + selectGroup($scope, selectedGroup.toLowerCase()); + } + + scrollToLintByURL($scope); + + setTimeout(function () { + var el = document.getElementById('filter-input'); + if (el) { el.focus() } + }, 0); + }) + .error(function (data) { + $scope.error = data; + $scope.loading = false; + }); + + window.addEventListener('hashchange', function () { + // trigger re-render + $timeout(function () { + $scope.levels = LEVEL_FILTERS_DEFAULT; + $scope.search = window.location.hash.slice(1); + $scope.open[window.location.hash.slice(1)] = true; + + scrollToLintByURL($scope); + }); + return true; + }, false); + }); +})(); + +function getQueryVariable(variable) { + var query = window.location.search.substring(1); + var vars = query.split('&'); + for (var i = 0; i < vars.length; i++) { + var pair = vars[i].split('='); + if (decodeURIComponent(pair[0]) == variable) { + return decodeURIComponent(pair[1]); + } + } +} + +function setTheme(theme, store) { + let enableHighlight = false; + let enableNight = false; + let enableAyu = false; + + if (theme == "ayu") { + enableAyu = true; + } else if (theme == "coal" || theme == "navy") { + enableNight = true; + } else if (theme == "rust") { + enableHighlight = true; + } else { + enableHighlight = true; + // this makes sure that an unknown theme request gets set to a known one + theme = "light"; + } + document.getElementsByTagName("body")[0].className = theme; + + document.getElementById("styleHighlight").disabled = !enableHighlight; + document.getElementById("styleNight").disabled = !enableNight; + document.getElementById("styleAyu").disabled = !enableAyu; + + if (store) { + try { + localStorage.setItem('clippy-lint-list-theme', theme); + } catch (e) { } + } +} + +// loading the theme after the initial load +setTheme(localStorage.getItem('clippy-lint-list-theme'), false); From fc965c7300bb05dc11461d7de509e88804392c74 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 21 May 2022 13:50:11 +0200 Subject: [PATCH 06/79] Fix lint registration --- clippy_lints/src/lib.register_lints.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 3350697ed7a41..5552ea8aa80ac 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -479,7 +479,6 @@ store.register_lints(&[ size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, stable_sort_primitive::STABLE_SORT_PRIMITIVE, - significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE, strings::STRING_ADD, strings::STRING_ADD_ASSIGN, strings::STRING_FROM_UTF8_AS_BYTES, From cbe6607422830fe154ca1203c38a3deeed945370 Mon Sep 17 00:00:00 2001 From: Jacob Pratt Date: Sat, 21 May 2022 13:53:26 -0400 Subject: [PATCH 07/79] Remove feature: `crate` visibility modifier --- clippy_utils/src/ast_utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index 7919800483f52..9aa3793520893 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -545,7 +545,7 @@ pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool { pub fn eq_vis(l: &Visibility, r: &Visibility) -> bool { use VisibilityKind::*; match (&l.kind, &r.kind) { - (Public, Public) | (Inherited, Inherited) | (Crate(_), Crate(_)) => true, + (Public, Public) | (Inherited, Inherited) | (Crate, Crate) => true, (Restricted { path: l, .. }, Restricted { path: r, .. }) => eq_path(l, r), _ => false, } From 855849034c15330f146376ae7073c38cbacb00b2 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Fri, 20 May 2022 20:33:31 +0000 Subject: [PATCH 08/79] get_last_with_len: lint VecDeque and any deref to slice previously only vecs were supported --- clippy_lints/src/get_last_with_len.rs | 107 ------------------ clippy_lints/src/lib.register_all.rs | 2 +- clippy_lints/src/lib.register_complexity.rs | 2 +- clippy_lints/src/lib.register_lints.rs | 2 +- clippy_lints/src/lib.rs | 2 - clippy_lints/src/methods/get_last_with_len.rs | 55 +++++++++ clippy_lints/src/methods/mod.rs | 35 ++++++ tests/ui/get_last_with_len.fixed | 28 ++++- tests/ui/get_last_with_len.rs | 28 ++++- tests/ui/get_last_with_len.stderr | 36 +++++- 10 files changed, 172 insertions(+), 125 deletions(-) delete mode 100644 clippy_lints/src/get_last_with_len.rs create mode 100644 clippy_lints/src/methods/get_last_with_len.rs diff --git a/clippy_lints/src/get_last_with_len.rs b/clippy_lints/src/get_last_with_len.rs deleted file mode 100644 index df29d9308e712..0000000000000 --- a/clippy_lints/src/get_last_with_len.rs +++ /dev/null @@ -1,107 +0,0 @@ -//! lint on using `x.get(x.len() - 1)` instead of `x.last()` - -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::SpanlessEq; -use if_chain::if_chain; -use rustc_ast::ast::LitKind; -use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Spanned; -use rustc_span::sym; - -declare_clippy_lint! { - /// ### What it does - /// Checks for using `x.get(x.len() - 1)` instead of - /// `x.last()`. - /// - /// ### Why is this bad? - /// Using `x.last()` is easier to read and has the same - /// result. - /// - /// Note that using `x[x.len() - 1]` is semantically different from - /// `x.last()`. Indexing into the array will panic on out-of-bounds - /// accesses, while `x.get()` and `x.last()` will return `None`. - /// - /// There is another lint (get_unwrap) that covers the case of using - /// `x.get(index).unwrap()` instead of `x[index]`. - /// - /// ### Example - /// ```rust - /// // Bad - /// let x = vec![2, 3, 5]; - /// let last_element = x.get(x.len() - 1); - /// - /// // Good - /// let x = vec![2, 3, 5]; - /// let last_element = x.last(); - /// ``` - #[clippy::version = "1.37.0"] - pub GET_LAST_WITH_LEN, - complexity, - "Using `x.get(x.len() - 1)` when `x.last()` is correct and simpler" -} - -declare_lint_pass!(GetLastWithLen => [GET_LAST_WITH_LEN]); - -impl<'tcx> LateLintPass<'tcx> for GetLastWithLen { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - // Is a method call - if let ExprKind::MethodCall(path, args, _) = expr.kind; - - // Method name is "get" - if path.ident.name == sym!(get); - - // Argument 0 (the struct we're calling the method on) is a vector - if let Some(struct_calling_on) = args.get(0); - let struct_ty = cx.typeck_results().expr_ty(struct_calling_on); - if is_type_diagnostic_item(cx, struct_ty, sym::Vec); - - // Argument to "get" is a subtraction - if let Some(get_index_arg) = args.get(1); - if let ExprKind::Binary( - Spanned { - node: BinOpKind::Sub, - .. - }, - lhs, - rhs, - ) = &get_index_arg.kind; - - // LHS of subtraction is "x.len()" - if let ExprKind::MethodCall(arg_lhs_path, lhs_args, _) = &lhs.kind; - if arg_lhs_path.ident.name == sym::len; - if let Some(arg_lhs_struct) = lhs_args.get(0); - - // The two vectors referenced (x in x.get(...) and in x.len()) - if SpanlessEq::new(cx).eq_expr(struct_calling_on, arg_lhs_struct); - - // RHS of subtraction is 1 - if let ExprKind::Lit(rhs_lit) = &rhs.kind; - if let LitKind::Int(1, ..) = rhs_lit.node; - - then { - let mut applicability = Applicability::MachineApplicable; - let vec_name = snippet_with_applicability( - cx, - struct_calling_on.span, "vec", - &mut applicability, - ); - - span_lint_and_sugg( - cx, - GET_LAST_WITH_LEN, - expr.span, - &format!("accessing last element with `{0}.get({0}.len() - 1)`", vec_name), - "try", - format!("{}.last()", vec_name), - applicability, - ); - } - } - } -} diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index be5c478900fac..87279de05c00a 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -91,7 +91,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF), LintId::of(functions::RESULT_UNIT_ERR), LintId::of(functions::TOO_MANY_ARGUMENTS), - LintId::of(get_last_with_len::GET_LAST_WITH_LEN), LintId::of(identity_op::IDENTITY_OP), LintId::of(if_let_mutex::IF_LET_MUTEX), LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING), @@ -166,6 +165,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(methods::FILTER_MAP_IDENTITY), LintId::of(methods::FILTER_NEXT), LintId::of(methods::FLAT_MAP_IDENTITY), + LintId::of(methods::GET_LAST_WITH_LEN), LintId::of(methods::INSPECT_FOR_EACH), LintId::of(methods::INTO_ITER_ON_REF), LintId::of(methods::IS_DIGIT_ASCII_RADIX), diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs index b15c979d0c70e..d5dfcd10a6683 100644 --- a/clippy_lints/src/lib.register_complexity.rs +++ b/clippy_lints/src/lib.register_complexity.rs @@ -15,7 +15,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(explicit_write::EXPLICIT_WRITE), LintId::of(format::USELESS_FORMAT), LintId::of(functions::TOO_MANY_ARGUMENTS), - LintId::of(get_last_with_len::GET_LAST_WITH_LEN), LintId::of(identity_op::IDENTITY_OP), LintId::of(int_plus_one::INT_PLUS_ONE), LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES), @@ -37,6 +36,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(methods::FILTER_MAP_IDENTITY), LintId::of(methods::FILTER_NEXT), LintId::of(methods::FLAT_MAP_IDENTITY), + LintId::of(methods::GET_LAST_WITH_LEN), LintId::of(methods::INSPECT_FOR_EACH), LintId::of(methods::ITER_COUNT), LintId::of(methods::MANUAL_FILTER_MAP), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 5552ea8aa80ac..527ff3485f2ac 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -183,7 +183,6 @@ store.register_lints(&[ functions::TOO_MANY_ARGUMENTS, functions::TOO_MANY_LINES, future_not_send::FUTURE_NOT_SEND, - get_last_with_len::GET_LAST_WITH_LEN, identity_op::IDENTITY_OP, if_let_mutex::IF_LET_MUTEX, if_not_else::IF_NOT_ELSE, @@ -302,6 +301,7 @@ store.register_lints(&[ methods::FLAT_MAP_IDENTITY, methods::FLAT_MAP_OPTION, methods::FROM_ITER_INSTEAD_OF_COLLECT, + methods::GET_LAST_WITH_LEN, methods::GET_UNWRAP, methods::IMPLICIT_CLONE, methods::INEFFICIENT_TO_STRING, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 4ac834f72405b..2f8533723efc5 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -242,7 +242,6 @@ mod from_over_into; mod from_str_radix_10; mod functions; mod future_not_send; -mod get_last_with_len; mod identity_op; mod if_let_mutex; mod if_not_else; @@ -652,7 +651,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(strings::StringLitAsBytes)); store.register_late_pass(|| Box::new(derive::Derive)); store.register_late_pass(|| Box::new(derivable_impls::DerivableImpls)); - store.register_late_pass(|| Box::new(get_last_with_len::GetLastWithLen)); store.register_late_pass(|| Box::new(drop_forget_ref::DropForgetRef)); store.register_late_pass(|| Box::new(empty_enum::EmptyEnum)); store.register_late_pass(|| Box::new(absurd_extreme_comparisons::AbsurdExtremeComparisons)); diff --git a/clippy_lints/src/methods/get_last_with_len.rs b/clippy_lints/src/methods/get_last_with_len.rs new file mode 100644 index 0000000000000..23368238ef5cc --- /dev/null +++ b/clippy_lints/src/methods/get_last_with_len.rs @@ -0,0 +1,55 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::SpanlessEq; +use rustc_ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::source_map::Spanned; +use rustc_span::sym; + +use super::GET_LAST_WITH_LEN; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) { + // Argument to "get" is a subtraction + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Sub, .. + }, + lhs, + rhs, + ) = arg.kind + + // LHS of subtraction is "x.len()" + && let ExprKind::MethodCall(lhs_path, [lhs_recv], _) = &lhs.kind + && lhs_path.ident.name == sym::len + + // RHS of subtraction is 1 + && let ExprKind::Lit(rhs_lit) = &rhs.kind + && let LitKind::Int(1, ..) = rhs_lit.node + + // check that recv == lhs_recv `recv.get(lhs_recv.len() - 1)` + && SpanlessEq::new(cx).eq_expr(recv, lhs_recv) + && !recv.can_have_side_effects() + { + let method = match cx.typeck_results().expr_ty_adjusted(recv).peel_refs().kind() { + ty::Adt(def, _) if cx.tcx.is_diagnostic_item(sym::VecDeque, def.did()) => "back", + ty::Slice(_) => "last", + _ => return, + }; + + let mut applicability = Applicability::MachineApplicable; + let recv_snippet = snippet_with_applicability(cx, recv.span, "_", &mut applicability); + + span_lint_and_sugg( + cx, + GET_LAST_WITH_LEN, + expr.span, + &format!("accessing last element with `{recv_snippet}.get({recv_snippet}.len() - 1)`"), + "try", + format!("{recv_snippet}.{method}()"), + applicability, + ); + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 35fc452ed7cf0..8e89f43e207cc 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -21,6 +21,7 @@ mod filter_next; mod flat_map_identity; mod flat_map_option; mod from_iter_instead_of_collect; +mod get_last_with_len; mod get_unwrap; mod implicit_clone; mod inefficient_to_string; @@ -1209,6 +1210,38 @@ declare_clippy_lint! { "replace `.drain(..)` with `.into_iter()`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for using `x.get(x.len() - 1)` instead of + /// `x.last()`. + /// + /// ### Why is this bad? + /// Using `x.last()` is easier to read and has the same + /// result. + /// + /// Note that using `x[x.len() - 1]` is semantically different from + /// `x.last()`. Indexing into the array will panic on out-of-bounds + /// accesses, while `x.get()` and `x.last()` will return `None`. + /// + /// There is another lint (get_unwrap) that covers the case of using + /// `x.get(index).unwrap()` instead of `x[index]`. + /// + /// ### Example + /// ```rust + /// // Bad + /// let x = vec![2, 3, 5]; + /// let last_element = x.get(x.len() - 1); + /// + /// // Good + /// let x = vec![2, 3, 5]; + /// let last_element = x.last(); + /// ``` + #[clippy::version = "1.37.0"] + pub GET_LAST_WITH_LEN, + complexity, + "Using `x.get(x.len() - 1)` when `x.last()` is correct and simpler" +} + declare_clippy_lint! { /// ### What it does /// Checks for use of `.get().unwrap()` (or @@ -2264,6 +2297,7 @@ impl_lint_pass!(Methods => [ BYTES_NTH, ITER_SKIP_NEXT, GET_UNWRAP, + GET_LAST_WITH_LEN, STRING_EXTEND_CHARS, ITER_CLONED_COLLECT, ITER_WITH_DRAIN, @@ -2590,6 +2624,7 @@ impl Methods { inspect_for_each::check(cx, expr, span2); } }, + ("get", [arg]) => get_last_with_len::check(cx, expr, recv, arg), ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"), ("is_file", []) => filetype_is_file::check(cx, expr, recv), ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv), diff --git a/tests/ui/get_last_with_len.fixed b/tests/ui/get_last_with_len.fixed index c8b363f9c38e6..1e90b37687a15 100644 --- a/tests/ui/get_last_with_len.fixed +++ b/tests/ui/get_last_with_len.fixed @@ -1,10 +1,13 @@ // run-rustfix #![warn(clippy::get_last_with_len)] +#![allow(unused)] + +use std::collections::VecDeque; fn dont_use_last() { let x = vec![2, 3, 5]; - let _ = x.last(); // ~ERROR Use x.last() + let _ = x.last(); } fn indexing_two_from_end() { @@ -23,9 +26,24 @@ fn use_last_with_different_vec_length() { let _ = x.get(y.len() - 1); } +struct S { + field: Vec, +} + +fn in_field(s: &S) { + let _ = s.field.last(); +} + fn main() { - dont_use_last(); - indexing_two_from_end(); - index_into_last(); - use_last_with_different_vec_length(); + let slice = &[1, 2, 3]; + let _ = slice.last(); + + let array = [4, 5, 6]; + let _ = array.last(); + + let deq = VecDeque::from([7, 8, 9]); + let _ = deq.back(); + + let nested = [[1]]; + let _ = nested[0].last(); } diff --git a/tests/ui/get_last_with_len.rs b/tests/ui/get_last_with_len.rs index bf9cb2d7e0ccd..d63a731bd5246 100644 --- a/tests/ui/get_last_with_len.rs +++ b/tests/ui/get_last_with_len.rs @@ -1,10 +1,13 @@ // run-rustfix #![warn(clippy::get_last_with_len)] +#![allow(unused)] + +use std::collections::VecDeque; fn dont_use_last() { let x = vec![2, 3, 5]; - let _ = x.get(x.len() - 1); // ~ERROR Use x.last() + let _ = x.get(x.len() - 1); } fn indexing_two_from_end() { @@ -23,9 +26,24 @@ fn use_last_with_different_vec_length() { let _ = x.get(y.len() - 1); } +struct S { + field: Vec, +} + +fn in_field(s: &S) { + let _ = s.field.get(s.field.len() - 1); +} + fn main() { - dont_use_last(); - indexing_two_from_end(); - index_into_last(); - use_last_with_different_vec_length(); + let slice = &[1, 2, 3]; + let _ = slice.get(slice.len() - 1); + + let array = [4, 5, 6]; + let _ = array.get(array.len() - 1); + + let deq = VecDeque::from([7, 8, 9]); + let _ = deq.get(deq.len() - 1); + + let nested = [[1]]; + let _ = nested[0].get(nested[0].len() - 1); } diff --git a/tests/ui/get_last_with_len.stderr b/tests/ui/get_last_with_len.stderr index 55baf87384a24..ac8dd6c2e41a8 100644 --- a/tests/ui/get_last_with_len.stderr +++ b/tests/ui/get_last_with_len.stderr @@ -1,10 +1,40 @@ error: accessing last element with `x.get(x.len() - 1)` - --> $DIR/get_last_with_len.rs:7:13 + --> $DIR/get_last_with_len.rs:10:13 | -LL | let _ = x.get(x.len() - 1); // ~ERROR Use x.last() +LL | let _ = x.get(x.len() - 1); | ^^^^^^^^^^^^^^^^^^ help: try: `x.last()` | = note: `-D clippy::get-last-with-len` implied by `-D warnings` -error: aborting due to previous error +error: accessing last element with `s.field.get(s.field.len() - 1)` + --> $DIR/get_last_with_len.rs:34:13 + | +LL | let _ = s.field.get(s.field.len() - 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.field.last()` + +error: accessing last element with `slice.get(slice.len() - 1)` + --> $DIR/get_last_with_len.rs:39:13 + | +LL | let _ = slice.get(slice.len() - 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `slice.last()` + +error: accessing last element with `array.get(array.len() - 1)` + --> $DIR/get_last_with_len.rs:42:13 + | +LL | let _ = array.get(array.len() - 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `array.last()` + +error: accessing last element with `deq.get(deq.len() - 1)` + --> $DIR/get_last_with_len.rs:45:13 + | +LL | let _ = deq.get(deq.len() - 1); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `deq.back()` + +error: accessing last element with `nested[0].get(nested[0].len() - 1)` + --> $DIR/get_last_with_len.rs:48:13 + | +LL | let _ = nested[0].get(nested[0].len() - 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `nested[0].last()` + +error: aborting due to 6 previous errors From a3199375bdcb7f6d1b6946dccfe2fee1dcf999d7 Mon Sep 17 00:00:00 2001 From: Jacob Pratt Date: Sat, 21 May 2022 14:45:14 -0400 Subject: [PATCH 09/79] Merge crate and restricted visibilities --- clippy_utils/src/ast_utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index 9aa3793520893..09e8fa4be41fd 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -545,7 +545,7 @@ pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool { pub fn eq_vis(l: &Visibility, r: &Visibility) -> bool { use VisibilityKind::*; match (&l.kind, &r.kind) { - (Public, Public) | (Inherited, Inherited) | (Crate, Crate) => true, + (Public, Public) | (Inherited, Inherited) => true, (Restricted { path: l, .. }, Restricted { path: r, .. }) => eq_path(l, r), _ => false, } From e885157f030cdfac377febefb6acd4d33c26aaba Mon Sep 17 00:00:00 2001 From: Ding Xiang Fei Date: Fri, 1 Apr 2022 21:12:18 +0800 Subject: [PATCH 10/79] factor out the rvalue lifetime rule remove region_scope_tree from RegionCtxt Apply suggestions from code review Co-authored-by: Niko Matsakis --- clippy_lints/src/loops/needless_range_loop.rs | 76 +++++++++++++------ clippy_lints/src/shadow.rs | 46 +++++------ 2 files changed, 76 insertions(+), 46 deletions(-) diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 09f9c05b4fced..e2b82f9fd02ad 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -3,7 +3,9 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::ty::has_iter_method; use clippy_utils::visitors::is_local_used; -use clippy_utils::{contains_name, higher, is_integer_const, match_trait_method, paths, sugg, SpanlessEq}; +use clippy_utils::{ + contains_name, higher, is_integer_const, match_trait_method, paths, sugg, SpanlessEq, +}; use if_chain::if_chain; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -27,12 +29,7 @@ pub(super) fn check<'tcx>( body: &'tcx Expr<'_>, expr: &'tcx Expr<'_>, ) { - if let Some(higher::Range { - start: Some(start), - ref end, - limits, - }) = higher::Range::hir(arg) - { + if let Some(higher::Range { start: Some(start), ref end, limits }) = higher::Range::hir(arg) { // the var must be a single name if let PatKind::Binding(_, canonical_id, ident, _) = pat.kind { let mut visitor = VarVisitor { @@ -58,7 +55,11 @@ pub(super) fn check<'tcx>( // ensure that the indexed variable was declared before the loop, see #601 if let Some(indexed_extent) = indexed_extent { let parent_def_id = cx.tcx.hir().get_parent_item(expr.hir_id); - let region_scope_tree = cx.tcx.region_scope_tree(parent_def_id); + let parent_body_id = cx + .tcx + .hir() + .body_owned_by(cx.tcx.hir().local_def_id_to_hir_id(parent_def_id)); + let region_scope_tree = &cx.tcx.typeck_body(parent_body_id).region_scope_tree; let pat_extent = region_scope_tree.var_scope(pat.hir_id.local_id).unwrap(); if region_scope_tree.is_subscope_of(indexed_extent, pat_extent) { return; @@ -107,17 +108,22 @@ pub(super) fn check<'tcx>( } } - if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) { + if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) + { String::new() - } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr) { + } else if visitor.indexed_mut.contains(&indexed) + && contains_name(indexed, take_expr) + { return; } else { match limits { ast::RangeLimits::Closed => { let take_expr = sugg::Sugg::hir(cx, take_expr, ""); format!(".take({})", take_expr + sugg::ONE) - }, - ast::RangeLimits::HalfOpen => format!(".take({})", snippet(cx, take_expr.span, "..")), + } + ast::RangeLimits::HalfOpen => { + format!(".take({})", snippet(cx, take_expr.span, "..")) + } } } } else { @@ -143,7 +149,10 @@ pub(super) fn check<'tcx>( cx, NEEDLESS_RANGE_LOOP, arg.span, - &format!("the loop variable `{}` is used to index `{}`", ident.name, indexed), + &format!( + "the loop variable `{}` is used to index `{}`", + ident.name, indexed + ), |diag| { multispan_sugg( diag, @@ -152,7 +161,10 @@ pub(super) fn check<'tcx>( (pat.span, format!("({}, )", ident.name)), ( arg.span, - format!("{}.{}().enumerate(){}{}", indexed, method, method_1, method_2), + format!( + "{}.{}().enumerate(){}{}", + indexed, method, method_1, method_2 + ), ), ], ); @@ -169,7 +181,10 @@ pub(super) fn check<'tcx>( cx, NEEDLESS_RANGE_LOOP, arg.span, - &format!("the loop variable `{}` is only used to index `{}`", ident.name, indexed), + &format!( + "the loop variable `{}` is only used to index `{}`", + ident.name, indexed + ), |diag| { multispan_sugg( diag, @@ -246,7 +261,12 @@ struct VarVisitor<'a, 'tcx> { } impl<'a, 'tcx> VarVisitor<'a, 'tcx> { - fn check(&mut self, idx: &'tcx Expr<'_>, seqexpr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) -> bool { + fn check( + &mut self, + idx: &'tcx Expr<'_>, + seqexpr: &'tcx Expr<'_>, + expr: &'tcx Expr<'_>, + ) -> bool { if_chain! { // the indexed container is referenced by a name if let ExprKind::Path(ref seqpath) = seqexpr.kind; @@ -262,7 +282,16 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { match res { Res::Local(hir_id) => { let parent_def_id = self.cx.tcx.hir().get_parent_item(expr.hir_id); - let extent = self.cx.tcx.region_scope_tree(parent_def_id).var_scope(hir_id.local_id).unwrap(); + let parent_body_id = self.cx + .tcx + .hir() + .body_owned_by(self.cx.tcx.hir().local_def_id_to_hir_id(parent_def_id)); + let extent = self.cx + .tcx + .typeck_body(parent_body_id) + .region_scope_tree + .var_scope(hir_id.local_id) + .unwrap(); if index_used_directly { self.indexed_directly.insert( seqvar.segments[0].ident.name, @@ -331,13 +360,13 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { self.visit_expr(lhs); self.prefer_mutable = false; self.visit_expr(rhs); - }, + } ExprKind::AddrOf(BorrowKind::Ref, mutbl, expr) => { if mutbl == Mutability::Mut { self.prefer_mutable = true; } self.visit_expr(expr); - }, + } ExprKind::Call(f, args) => { self.visit_expr(f); for expr in args { @@ -350,10 +379,11 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { } self.visit_expr(expr); } - }, + } ExprKind::MethodCall(_, args, _) => { let def_id = self.cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); - for (ty, expr) in iter::zip(self.cx.tcx.fn_sig(def_id).inputs().skip_binder(), args) { + for (ty, expr) in iter::zip(self.cx.tcx.fn_sig(def_id).inputs().skip_binder(), args) + { self.prefer_mutable = false; if let ty::Ref(_, _, mutbl) = *ty.kind() { if mutbl == Mutability::Mut { @@ -362,11 +392,11 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { } self.visit_expr(expr); } - }, + } ExprKind::Closure(_, _, body_id, ..) => { let body = self.cx.tcx.hir().body(body_id); self.visit_expr(&body.value); - }, + } _ => walk_expr(self, expr), } self.prefer_mutable = old; diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 1ab7f52110ce7..db32b8d740b3e 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -5,7 +5,9 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::Res; use rustc_hir::def_id::LocalDefId; use rustc_hir::hir_id::ItemLocalId; -use rustc_hir::{Block, Body, BodyOwnerKind, Expr, ExprKind, HirId, Let, Node, Pat, PatKind, QPath, UnOp}; +use rustc_hir::{ + Block, Body, BodyOwnerKind, Expr, ExprKind, HirId, Let, Node, Pat, PatKind, QPath, UnOp, +}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{Span, Symbol}; @@ -139,27 +141,31 @@ impl<'tcx> LateLintPass<'tcx> for Shadow { fn check_body(&mut self, cx: &LateContext<'_>, body: &Body<'_>) { let hir = cx.tcx.hir(); - if !matches!( - hir.body_owner_kind(hir.body_owner_def_id(body.id())), - BodyOwnerKind::Closure - ) { + if !matches!(hir.body_owner_kind(hir.body_owner_def_id(body.id())), BodyOwnerKind::Closure) + { self.bindings.push(FxHashMap::default()); } } fn check_body_post(&mut self, cx: &LateContext<'_>, body: &Body<'_>) { let hir = cx.tcx.hir(); - if !matches!( - hir.body_owner_kind(hir.body_owner_def_id(body.id())), - BodyOwnerKind::Closure - ) { + if !matches!(hir.body_owner_kind(hir.body_owner_def_id(body.id())), BodyOwnerKind::Closure) + { self.bindings.pop(); } } } -fn is_shadow(cx: &LateContext<'_>, owner: LocalDefId, first: ItemLocalId, second: ItemLocalId) -> bool { - let scope_tree = cx.tcx.region_scope_tree(owner.to_def_id()); +fn is_shadow( + cx: &LateContext<'_>, + owner: LocalDefId, + first: ItemLocalId, + second: ItemLocalId, +) -> bool { + let scope_tree = &cx + .tcx + .typeck_body(cx.tcx.hir().body_owned_by(cx.tcx.hir().local_def_id_to_hir_id(owner))) + .region_scope_tree; let first_scope = scope_tree.var_scope(first).unwrap(); let second_scope = scope_tree.var_scope(second).unwrap(); scope_tree.is_subscope_of(second_scope, first_scope) @@ -174,15 +180,16 @@ fn lint_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, shadowed: HirId, span: Span) snippet(cx, expr.span, "..") ); (SHADOW_SAME, msg) - }, + } Some(expr) if is_local_used(cx, expr, shadowed) => { let msg = format!("`{}` is shadowed", snippet(cx, pat.span, "_")); (SHADOW_REUSE, msg) - }, + } _ => { - let msg = format!("`{}` shadows a previous, unrelated binding", snippet(cx, pat.span, "_")); + let msg = + format!("`{}` shadows a previous, unrelated binding", snippet(cx, pat.span, "_")); (SHADOW_UNRELATED, msg) - }, + } }; span_lint_and_note( cx, @@ -211,14 +218,7 @@ fn is_self_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, mut expr: &Expr<'_>, hir_ expr = match expr.kind { ExprKind::Box(e) | ExprKind::AddrOf(_, _, e) - | ExprKind::Block( - &Block { - stmts: [], - expr: Some(e), - .. - }, - _, - ) + | ExprKind::Block(&Block { stmts: [], expr: Some(e), .. }, _) | ExprKind::Unary(UnOp::Deref, e) => e, ExprKind::Path(QPath::Resolved(None, path)) => break path.res == Res::Local(hir_id), _ => break false, From 5cf07c6d29829714b929ecc32c6bbcb976cf0adb Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 22 May 2022 12:19:10 -0400 Subject: [PATCH 11/79] Set correct `ParamEnv` for `derive_partial_eq_without_eq` --- clippy_lints/src/derive.rs | 47 +++++++++++++++++---- clippy_utils/src/ty.rs | 22 +++++++--- tests/ui/derive_partial_eq_without_eq.fixed | 6 +++ tests/ui/derive_partial_eq_without_eq.rs | 6 +++ 4 files changed, 67 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index fe99f4a8d55d1..99347ebadc602 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -1,16 +1,17 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::paths; -use clippy_utils::ty::{implements_trait, is_copy}; +use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy}; use clippy_utils::{is_lint_allowed, match_def_path}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor}; use rustc_hir::{ - BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, TraitRef, UnsafeSource, Unsafety, + self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, UnsafeSource, Unsafety, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::subst::GenericArg; +use rustc_middle::ty::{self, BoundConstness, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, TraitRef, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::sym; @@ -224,7 +225,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive { fn check_hash_peq<'tcx>( cx: &LateContext<'tcx>, span: Span, - trait_ref: &TraitRef<'_>, + trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>, hash_is_automatically_derived: bool, ) { @@ -277,7 +278,7 @@ fn check_hash_peq<'tcx>( fn check_ord_partial_ord<'tcx>( cx: &LateContext<'tcx>, span: Span, - trait_ref: &TraitRef<'_>, + trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>, ord_is_automatically_derived: bool, ) { @@ -328,7 +329,7 @@ fn check_ord_partial_ord<'tcx>( } /// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint. -fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &TraitRef<'_>, ty: Ty<'tcx>) { +fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { let clone_id = match cx.tcx.lang_items().clone_trait() { Some(id) if trait_ref.trait_def_id() == Some(id) => id, _ => return, @@ -378,7 +379,7 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &T fn check_unsafe_derive_deserialize<'tcx>( cx: &LateContext<'tcx>, item: &Item<'_>, - trait_ref: &TraitRef<'_>, + trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>, ) { fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool { @@ -455,13 +456,41 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { } /// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint. -fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &TraitRef<'_>, ty: Ty<'tcx>) { +fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { if_chain! { if let ty::Adt(adt, substs) = ty.kind(); if let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq); + if let Some(peq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::PartialEq); if let Some(def_id) = trait_ref.trait_def_id(); if cx.tcx.is_diagnostic_item(sym::PartialEq, def_id); - if !implements_trait(cx, ty, eq_trait_def_id, substs); + // New `ParamEnv` replacing `T: PartialEq` with `T: Eq` + let param_env = ParamEnv::new( + cx.tcx.mk_predicates(cx.param_env.caller_bounds().iter().map(|p| { + let kind = p.kind(); + match kind.skip_binder() { + PredicateKind::Trait(p) + if p.trait_ref.def_id == peq_trait_def_id + && p.trait_ref.substs.get(0) == p.trait_ref.substs.get(1) + && matches!(p.trait_ref.self_ty().kind(), ty::Param(_)) + && p.constness == BoundConstness::NotConst + && p.polarity == ImplPolarity::Positive => + { + cx.tcx.mk_predicate(kind.rebind(PredicateKind::Trait(TraitPredicate { + trait_ref: TraitRef::new( + eq_trait_def_id, + cx.tcx.mk_substs([GenericArg::from(p.trait_ref.self_ty())].into_iter()), + ), + constness: BoundConstness::NotConst, + polarity: ImplPolarity::Positive, + }))) + }, + _ => p, + } + })), + cx.param_env.reveal(), + cx.param_env.constness(), + ); + if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, substs); then { // If all of our fields implement `Eq`, we can implement `Eq` too for variant in adt.variants() { diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 07d3d2807634f..203f33d352976 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -13,7 +13,8 @@ use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst}; use rustc_middle::ty::{ - self, AdtDef, Binder, FnSig, IntTy, Predicate, PredicateKind, Ty, TyCtxt, TypeFoldable, UintTy, VariantDiscr, + self, AdtDef, Binder, FnSig, IntTy, ParamEnv, Predicate, PredicateKind, Ty, TyCtxt, TypeFoldable, UintTy, + VariantDiscr, }; use rustc_span::symbol::Ident; use rustc_span::{sym, Span, Symbol, DUMMY_SP}; @@ -151,18 +152,29 @@ pub fn implements_trait<'tcx>( ty: Ty<'tcx>, trait_id: DefId, ty_params: &[GenericArg<'tcx>], +) -> bool { + implements_trait_with_env(cx.tcx, cx.param_env, ty, trait_id, ty_params) +} + +/// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context. +pub fn implements_trait_with_env<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + ty: Ty<'tcx>, + trait_id: DefId, + ty_params: &[GenericArg<'tcx>], ) -> bool { // Clippy shouldn't have infer types assert!(!ty.needs_infer()); - let ty = cx.tcx.erase_regions(ty); + let ty = tcx.erase_regions(ty); if ty.has_escaping_bound_vars() { return false; } - let ty_params = cx.tcx.mk_substs(ty_params.iter()); - cx.tcx.infer_ctxt().enter(|infcx| { + let ty_params = tcx.mk_substs(ty_params.iter()); + tcx.infer_ctxt().enter(|infcx| { infcx - .type_implements_trait(trait_id, ty, ty_params, cx.param_env) + .type_implements_trait(trait_id, ty, ty_params, param_env) .must_apply_modulo_regions() }) } diff --git a/tests/ui/derive_partial_eq_without_eq.fixed b/tests/ui/derive_partial_eq_without_eq.fixed index 7d4d1b3b64906..012780258fc3a 100644 --- a/tests/ui/derive_partial_eq_without_eq.fixed +++ b/tests/ui/derive_partial_eq_without_eq.fixed @@ -95,4 +95,10 @@ enum EnumNotEq { #[derive(Debug, PartialEq, Eq, Clone)] struct RustFixWithOtherDerives; +#[derive(PartialEq)] +struct Generic(T); + +#[derive(PartialEq, Eq)] +struct GenericPhantom(core::marker::PhantomData); + fn main() {} diff --git a/tests/ui/derive_partial_eq_without_eq.rs b/tests/ui/derive_partial_eq_without_eq.rs index ab4e1df1ca408..fc8285b0c6b75 100644 --- a/tests/ui/derive_partial_eq_without_eq.rs +++ b/tests/ui/derive_partial_eq_without_eq.rs @@ -95,4 +95,10 @@ enum EnumNotEq { #[derive(Debug, PartialEq, Clone)] struct RustFixWithOtherDerives; +#[derive(PartialEq)] +struct Generic(T); + +#[derive(PartialEq, Eq)] +struct GenericPhantom(core::marker::PhantomData); + fn main() {} From 0fd03a85deb7f423f4b0f869b1e9ea20bb9b748e Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sun, 22 May 2022 15:24:47 -0400 Subject: [PATCH 12/79] Strip `clippy::` prefix from search strings --- util/gh-pages/script.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/util/gh-pages/script.js b/util/gh-pages/script.js index 68e65acf9cb7e..6909fbcae09ff 100644 --- a/util/gh-pages/script.js +++ b/util/gh-pages/script.js @@ -232,6 +232,9 @@ return true; } searchStr = searchStr.toLowerCase(); + if (searchStr.startsWith("clippy::")) { + searchStr = searchStr.slice(8); + } // Search by id if (lint.id.indexOf(searchStr.replace("-", "_")) !== -1) { From 8ec9e5e1616efab820d0a3c4adcf3b76c2663f77 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 22 May 2022 17:39:44 -0700 Subject: [PATCH 13/79] Fix clippy explicit_write lint for new writeln implementation --- clippy_lints/src/explicit_write.rs | 36 ++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index 3e2217c28da3a..d8f765b288a6c 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -4,7 +4,8 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_expn_of, match_function_call, paths}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::def::Res; +use rustc_hir::{BindingAnnotation, Block, BlockCheckMode, Expr, ExprKind, Node, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -39,7 +40,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { if let ExprKind::MethodCall(unwrap_fun, [write_call], _) = expr.kind; if unwrap_fun.ident.name == sym::unwrap; // match call to write_fmt - if let ExprKind::MethodCall(write_fun, [write_recv, write_arg], _) = write_call.kind; + if let ExprKind::MethodCall(write_fun, [write_recv, write_arg], _) = look_in_block(cx, &write_call.kind); if write_fun.ident.name == sym!(write_fmt); // match calls to std::io::stdout() / std::io::stderr () if let Some(dest_name) = if match_function_call(cx, write_recv, &paths::STDOUT).is_some() { @@ -100,3 +101,34 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { } } } + +/// If `kind` is a block that looks like `{ let result = $expr; result }` then +/// returns $expr. Otherwise returns `kind`. +fn look_in_block<'tcx, 'hir>(cx: &LateContext<'tcx>, kind: &'tcx ExprKind<'hir>) -> &'tcx ExprKind<'hir> { + if_chain! { + if let ExprKind::Block(block, _label @ None) = kind; + if let Block { + stmts: [Stmt { kind: StmtKind::Local(local), .. }], + expr: Some(expr_end_of_block), + rules: BlockCheckMode::DefaultBlock, + .. + } = block; + + // Find id of the local that expr_end_of_block resolves to + if let ExprKind::Path(QPath::Resolved(None, expr_path)) = expr_end_of_block.kind; + if let Res::Local(expr_res) = expr_path.res; + if let Some(Node::Binding(res_pat)) = cx.tcx.hir().find(expr_res); + + // Find id of the local we found in the block + if let PatKind::Binding(BindingAnnotation::Unannotated, local_hir_id, _ident, None) = local.pat.kind; + + // If those two are the same hir id + if res_pat.hir_id == local_hir_id; + + if let Some(init) = local.init; + then { + return &init.kind; + } + } + kind +} From 9b55ea78c83d9cb96b3d4ca6a2c4d1a6c7631730 Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 23 May 2022 09:23:35 +0200 Subject: [PATCH 14/79] update dependencies --- Cargo.toml | 4 +- clippy_dev/Cargo.toml | 2 +- clippy_dev/src/main.rs | 156 ++++++++++++++++++---------------------- clippy_dev/src/serve.rs | 10 ++- lintcheck/Cargo.toml | 2 +- lintcheck/src/config.rs | 22 +++--- 6 files changed, 89 insertions(+), 107 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 373e720b0d5c0..3c8b758d53dca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ tempfile = { version = "3.2", optional = true } termize = "0.1" [dev-dependencies] -compiletest_rs = { version = "0.7.1", features = ["tmp"] } +compiletest_rs = { version = "0.8", features = ["tmp"] } tester = "0.9" regex = "1.5" # This is used by the `collect-metadata` alias. @@ -48,7 +48,7 @@ quote = "1.0" serde = { version = "1.0.125", features = ["derive"] } syn = { version = "1.0", features = ["full"] } futures = "0.3" -parking_lot = "0.11.2" +parking_lot = "0.12" tokio = { version = "1", features = ["io-util"] } rustc-semver = "1.1" diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml index 2cfbcea5034e6..b0d470a2124d8 100644 --- a/clippy_dev/Cargo.toml +++ b/clippy_dev/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] aho-corasick = "0.7" -clap = "2.33" +clap = "3.1" indoc = "1.0" itertools = "0.10.1" opener = "0.5" diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index d5cd7ca96c0c0..ee535b1d3be8b 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -2,20 +2,20 @@ // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] -use clap::{App, AppSettings, Arg, ArgMatches, SubCommand}; +use clap::{Arg, ArgMatches, Command}; use clippy_dev::{bless, fmt, lint, new_lint, serve, setup, update_lints}; use indoc::indoc; fn main() { let matches = get_clap_config(); match matches.subcommand() { - ("bless", Some(matches)) => { + Some(("bless", matches)) => { bless::bless(matches.is_present("ignore-timestamp")); }, - ("fmt", Some(matches)) => { + Some(("fmt", matches)) => { fmt::run(matches.is_present("check"), matches.is_present("verbose")); }, - ("update_lints", Some(matches)) => { + Some(("update_lints", matches)) => { if matches.is_present("print-only") { update_lints::print_lints(); } else if matches.is_present("check") { @@ -24,7 +24,7 @@ fn main() { update_lints::update(update_lints::UpdateMode::Change); } }, - ("new_lint", Some(matches)) => { + Some(("new_lint", matches)) => { match new_lint::create( matches.value_of("pass"), matches.value_of("name"), @@ -35,8 +35,8 @@ fn main() { Err(e) => eprintln!("Unable to create lint: {}", e), } }, - ("setup", Some(sub_command)) => match sub_command.subcommand() { - ("intellij", Some(matches)) => { + Some(("setup", sub_command)) => match sub_command.subcommand() { + Some(("intellij", matches)) => { if matches.is_present("remove") { setup::intellij::remove_rustc_src(); } else { @@ -47,14 +47,14 @@ fn main() { ); } }, - ("git-hook", Some(matches)) => { + Some(("git-hook", matches)) => { if matches.is_present("remove") { setup::git_hook::remove_hook(); } else { setup::git_hook::install_hook(matches.is_present("force-override")); } }, - ("vscode-tasks", Some(matches)) => { + Some(("vscode-tasks", matches)) => { if matches.is_present("remove") { setup::vscode::remove_tasks(); } else { @@ -63,23 +63,23 @@ fn main() { }, _ => {}, }, - ("remove", Some(sub_command)) => match sub_command.subcommand() { - ("git-hook", Some(_)) => setup::git_hook::remove_hook(), - ("intellij", Some(_)) => setup::intellij::remove_rustc_src(), - ("vscode-tasks", Some(_)) => setup::vscode::remove_tasks(), + Some(("remove", sub_command)) => match sub_command.subcommand() { + Some(("git-hook", _)) => setup::git_hook::remove_hook(), + Some(("intellij", _)) => setup::intellij::remove_rustc_src(), + Some(("vscode-tasks", _)) => setup::vscode::remove_tasks(), _ => {}, }, - ("serve", Some(matches)) => { + Some(("serve", matches)) => { let port = matches.value_of("port").unwrap().parse().unwrap(); let lint = matches.value_of("lint"); serve::run(port, lint); }, - ("lint", Some(matches)) => { + Some(("lint", matches)) => { let path = matches.value_of("path").unwrap(); let args = matches.values_of("args").into_iter().flatten(); lint::run(path, args); }, - ("rename_lint", Some(matches)) => { + Some(("rename_lint", matches)) => { let old_name = matches.value_of("old_name").unwrap(); let new_name = matches.value_of("new_name").unwrap_or(old_name); let uplift = matches.is_present("uplift"); @@ -89,35 +89,24 @@ fn main() { } } -fn get_clap_config<'a>() -> ArgMatches<'a> { - App::new("Clippy developer tooling") - .setting(AppSettings::ArgRequiredElseHelp) +fn get_clap_config() -> ArgMatches { + Command::new("Clippy developer tooling") + .arg_required_else_help(true) .subcommand( - SubCommand::with_name("bless") - .about("bless the test output changes") - .arg( - Arg::with_name("ignore-timestamp") - .long("ignore-timestamp") - .help("Include files updated before clippy was built"), - ), + Command::new("bless").about("bless the test output changes").arg( + Arg::new("ignore-timestamp") + .long("ignore-timestamp") + .help("Include files updated before clippy was built"), + ), ) .subcommand( - SubCommand::with_name("fmt") + Command::new("fmt") .about("Run rustfmt on all projects and tests") - .arg( - Arg::with_name("check") - .long("check") - .help("Use the rustfmt --check option"), - ) - .arg( - Arg::with_name("verbose") - .short("v") - .long("verbose") - .help("Echo commands run"), - ), + .arg(Arg::new("check").long("check").help("Use the rustfmt --check option")) + .arg(Arg::new("verbose").short('v').long("verbose").help("Echo commands run")), ) .subcommand( - SubCommand::with_name("update_lints") + Command::new("update_lints") .about("Updates lint registration and information from the source code") .long_about( "Makes sure that:\n \ @@ -127,23 +116,23 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod`\n \ * all lints are registered in the lint store", ) - .arg(Arg::with_name("print-only").long("print-only").help( + .arg(Arg::new("print-only").long("print-only").help( "Print a table of lints to STDOUT. \ This does not include deprecated and internal lints. \ (Does not modify any files)", )) .arg( - Arg::with_name("check") + Arg::new("check") .long("check") .help("Checks that `cargo dev update_lints` has been run. Used on CI."), ), ) .subcommand( - SubCommand::with_name("new_lint") + Command::new("new_lint") .about("Create new lint and run `cargo dev update_lints`") .arg( - Arg::with_name("pass") - .short("p") + Arg::new("pass") + .short('p') .long("pass") .help("Specify whether the lint runs during the early or late pass") .takes_value(true) @@ -151,16 +140,16 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { .required(true), ) .arg( - Arg::with_name("name") - .short("n") + Arg::new("name") + .short('n') .long("name") .help("Name of the new lint in snake case, ex: fn_too_long") .takes_value(true) .required(true), ) .arg( - Arg::with_name("category") - .short("c") + Arg::new("category") + .short('c') .long("category") .help("What category the lint belongs to") .default_value("nursery") @@ -179,29 +168,25 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { ]) .takes_value(true), ) - .arg( - Arg::with_name("msrv") - .long("msrv") - .help("Add MSRV config code to the lint"), - ), + .arg(Arg::new("msrv").long("msrv").help("Add MSRV config code to the lint")), ) .subcommand( - SubCommand::with_name("setup") + Command::new("setup") .about("Support for setting up your personal development environment") - .setting(AppSettings::ArgRequiredElseHelp) + .arg_required_else_help(true) .subcommand( - SubCommand::with_name("intellij") + Command::new("intellij") .about("Alter dependencies so Intellij Rust can find rustc internals") .arg( - Arg::with_name("remove") + Arg::new("remove") .long("remove") .help("Remove the dependencies added with 'cargo dev setup intellij'") .required(false), ) .arg( - Arg::with_name("rustc-repo-path") + Arg::new("rustc-repo-path") .long("repo-path") - .short("r") + .short('r') .help("The path to a rustc repo that will be used for setting the dependencies") .takes_value(true) .value_name("path") @@ -210,66 +195,65 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { ), ) .subcommand( - SubCommand::with_name("git-hook") + Command::new("git-hook") .about("Add a pre-commit git hook that formats your code to make it look pretty") .arg( - Arg::with_name("remove") + Arg::new("remove") .long("remove") .help("Remove the pre-commit hook added with 'cargo dev setup git-hook'") .required(false), ) .arg( - Arg::with_name("force-override") + Arg::new("force-override") .long("force-override") - .short("f") + .short('f') .help("Forces the override of an existing git pre-commit hook") .required(false), ), ) .subcommand( - SubCommand::with_name("vscode-tasks") + Command::new("vscode-tasks") .about("Add several tasks to vscode for formatting, validation and testing") .arg( - Arg::with_name("remove") + Arg::new("remove") .long("remove") .help("Remove the tasks added with 'cargo dev setup vscode-tasks'") .required(false), ) .arg( - Arg::with_name("force-override") + Arg::new("force-override") .long("force-override") - .short("f") + .short('f') .help("Forces the override of existing vscode tasks") .required(false), ), ), ) .subcommand( - SubCommand::with_name("remove") + Command::new("remove") .about("Support for undoing changes done by the setup command") - .setting(AppSettings::ArgRequiredElseHelp) - .subcommand(SubCommand::with_name("git-hook").about("Remove any existing pre-commit git hook")) - .subcommand(SubCommand::with_name("vscode-tasks").about("Remove any existing vscode tasks")) + .arg_required_else_help(true) + .subcommand(Command::new("git-hook").about("Remove any existing pre-commit git hook")) + .subcommand(Command::new("vscode-tasks").about("Remove any existing vscode tasks")) .subcommand( - SubCommand::with_name("intellij") - .about("Removes rustc source paths added via `cargo dev setup intellij`"), + Command::new("intellij").about("Removes rustc source paths added via `cargo dev setup intellij`"), ), ) .subcommand( - SubCommand::with_name("serve") + Command::new("serve") .about("Launch a local 'ALL the Clippy Lints' website in a browser") .arg( - Arg::with_name("port") + Arg::new("port") .long("port") - .short("p") + .short('p') .help("Local port for the http server") .default_value("8000") .validator_os(serve::validate_port), ) - .arg(Arg::with_name("lint").help("Which lint's page to load initially (optional)")), + .arg(Arg::new("lint").help("Which lint's page to load initially (optional)")), ) .subcommand( - SubCommand::with_name("lint") + Command::new("lint") .about("Manually run clippy on a file or package") .after_help(indoc! {" EXAMPLES @@ -288,33 +272,33 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { cargo dev lint ~/my-project -- -- -W clippy::pedantic "}) .arg( - Arg::with_name("path") + Arg::new("path") .required(true) .help("The path to a file or package directory to lint"), ) .arg( - Arg::with_name("args") - .multiple(true) + Arg::new("args") + .multiple_occurrences(true) .help("Pass extra arguments to cargo/clippy-driver"), ), ) .subcommand( - SubCommand::with_name("rename_lint") + Command::new("rename_lint") .about("Renames the given lint") .arg( - Arg::with_name("old_name") + Arg::new("old_name") .index(1) .required(true) .help("The name of the lint to rename"), ) .arg( - Arg::with_name("new_name") + Arg::new("new_name") .index(2) - .required_unless("uplift") + .required_unless_present("uplift") .help("The new name of the lint"), ) .arg( - Arg::with_name("uplift") + Arg::new("uplift") .long("uplift") .help("This lint will be uplifted into rustc"), ), diff --git a/clippy_dev/src/serve.rs b/clippy_dev/src/serve.rs index b36e2a28ee454..d55b1a354d003 100644 --- a/clippy_dev/src/serve.rs +++ b/clippy_dev/src/serve.rs @@ -1,4 +1,5 @@ -use std::ffi::{OsStr, OsString}; +use std::ffi::OsStr; +use std::num::ParseIntError; use std::path::Path; use std::process::Command; use std::thread; @@ -59,9 +60,6 @@ fn mtime(path: impl AsRef) -> SystemTime { } #[allow(clippy::missing_errors_doc)] -pub fn validate_port(arg: &OsStr) -> Result<(), OsString> { - match arg.to_string_lossy().parse::() { - Ok(_port) => Ok(()), - Err(err) => Err(OsString::from(err.to_string())), - } +pub fn validate_port(arg: &OsStr) -> Result<(), ParseIntError> { + arg.to_string_lossy().parse::().map(|_| ()) } diff --git a/lintcheck/Cargo.toml b/lintcheck/Cargo.toml index e63f65ce2f75d..504d58b5197a1 100644 --- a/lintcheck/Cargo.toml +++ b/lintcheck/Cargo.toml @@ -11,7 +11,7 @@ publish = false [dependencies] cargo_metadata = "0.14" -clap = "2.33" +clap = "3.1" flate2 = "1.0" rayon = "1.5.1" serde = { version = "1.0", features = ["derive"] } diff --git a/lintcheck/src/config.rs b/lintcheck/src/config.rs index de32b48436019..a6f93d2a1c0a6 100644 --- a/lintcheck/src/config.rs +++ b/lintcheck/src/config.rs @@ -1,47 +1,47 @@ -use clap::{App, Arg, ArgMatches}; +use clap::{Arg, ArgMatches, Command}; use std::env; use std::path::PathBuf; -fn get_clap_config<'a>() -> ArgMatches<'a> { - App::new("lintcheck") +fn get_clap_config() -> ArgMatches { + Command::new("lintcheck") .about("run clippy on a set of crates and check output") .arg( - Arg::with_name("only") + Arg::new("only") .takes_value(true) .value_name("CRATE") .long("only") .help("Only process a single crate of the list"), ) .arg( - Arg::with_name("crates-toml") + Arg::new("crates-toml") .takes_value(true) .value_name("CRATES-SOURCES-TOML-PATH") .long("crates-toml") .help("Set the path for a crates.toml where lintcheck should read the sources from"), ) .arg( - Arg::with_name("threads") + Arg::new("threads") .takes_value(true) .value_name("N") - .short("j") + .short('j') .long("jobs") .help("Number of threads to use, 0 automatic choice"), ) .arg( - Arg::with_name("fix") + Arg::new("fix") .long("--fix") .help("Runs cargo clippy --fix and checks if all suggestions apply"), ) .arg( - Arg::with_name("filter") + Arg::new("filter") .long("--filter") .takes_value(true) - .multiple(true) + .multiple_occurrences(true) .value_name("clippy_lint_name") .help("Apply a filter to only collect specified lints, this also overrides `allow` attributes"), ) .arg( - Arg::with_name("markdown") + Arg::new("markdown") .long("--markdown") .help("Change the reports table to use markdown links"), ) From a342f52f9169af747f81b27505e5759511db4667 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Mon, 23 May 2022 12:19:25 +0000 Subject: [PATCH 15/79] cast_abs_to_unsigned: do not remove cast if it's required --- .../src/casts/cast_abs_to_unsigned.rs | 46 +++++----- tests/ui/cast_abs_to_unsigned.fixed | 21 +++++ tests/ui/cast_abs_to_unsigned.rs | 21 +++++ tests/ui/cast_abs_to_unsigned.stderr | 92 ++++++++++++++++++- 4 files changed, 157 insertions(+), 23 deletions(-) diff --git a/clippy_lints/src/casts/cast_abs_to_unsigned.rs b/clippy_lints/src/casts/cast_abs_to_unsigned.rs index 6bac6bf83f8e5..64ea326b75a0d 100644 --- a/clippy_lints/src/casts/cast_abs_to_unsigned.rs +++ b/clippy_lints/src/casts/cast_abs_to_unsigned.rs @@ -1,11 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; use clippy_utils::{meets_msrv, msrvs}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{self, Ty}; use rustc_semver::RustcVersion; use super::CAST_ABS_TO_UNSIGNED; @@ -18,25 +17,28 @@ pub(super) fn check( cast_to: Ty<'_>, msrv: Option, ) { - if_chain! { - if meets_msrv(msrv, msrvs::UNSIGNED_ABS); - if cast_from.is_integral(); - if cast_to.is_integral(); - if cast_from.is_signed(); - if !cast_to.is_signed(); - if let ExprKind::MethodCall(method_path, args, _) = cast_expr.kind; - if let method_name = method_path.ident.name.as_str(); - if method_name == "abs"; - then { - span_lint_and_sugg( - cx, - CAST_ABS_TO_UNSIGNED, - expr.span, - &format!("casting the result of `{}::{}()` to {}", cast_from, method_name, cast_to), - "replace with", - format!("{}.unsigned_abs()", Sugg::hir(cx, &args[0], "..")), - Applicability::MachineApplicable, - ); - } + if meets_msrv(msrv, msrvs::UNSIGNED_ABS) + && let ty::Int(from) = cast_from.kind() + && let ty::Uint(to) = cast_to.kind() + && let ExprKind::MethodCall(method_path, args, _) = cast_expr.kind + && method_path.ident.name.as_str() == "abs" + { + let span = if from.bit_width() == to.bit_width() { + expr.span + } else { + // if the result of `.unsigned_abs` would be a different type, keep the cast + // e.g. `i64 -> usize`, `i16 -> u8` + cast_expr.span + }; + + span_lint_and_sugg( + cx, + CAST_ABS_TO_UNSIGNED, + span, + &format!("casting the result of `{cast_from}::abs()` to {cast_to}"), + "replace with", + format!("{}.unsigned_abs()", Sugg::hir(cx, &args[0], "..")), + Applicability::MachineApplicable, + ); } } diff --git a/tests/ui/cast_abs_to_unsigned.fixed b/tests/ui/cast_abs_to_unsigned.fixed index 4ec2465be06db..a68b32b097e85 100644 --- a/tests/ui/cast_abs_to_unsigned.fixed +++ b/tests/ui/cast_abs_to_unsigned.fixed @@ -5,4 +5,25 @@ fn main() { let x: i32 = -42; let y: u32 = x.unsigned_abs(); println!("The absolute value of {} is {}", x, y); + + let a: i32 = -3; + let _: usize = a.unsigned_abs() as usize; + let _: usize = a.unsigned_abs() as _; + let _ = a.unsigned_abs() as usize; + + let a: i64 = -3; + let _ = a.unsigned_abs() as usize; + let _ = a.unsigned_abs() as u8; + let _ = a.unsigned_abs() as u16; + let _ = a.unsigned_abs() as u32; + let _ = a.unsigned_abs(); + let _ = a.unsigned_abs() as u128; + + let a: isize = -3; + let _ = a.unsigned_abs(); + let _ = a.unsigned_abs() as u8; + let _ = a.unsigned_abs() as u16; + let _ = a.unsigned_abs() as u32; + let _ = a.unsigned_abs() as u64; + let _ = a.unsigned_abs() as u128; } diff --git a/tests/ui/cast_abs_to_unsigned.rs b/tests/ui/cast_abs_to_unsigned.rs index 59b9c8c367883..110fbc6c2dfb6 100644 --- a/tests/ui/cast_abs_to_unsigned.rs +++ b/tests/ui/cast_abs_to_unsigned.rs @@ -5,4 +5,25 @@ fn main() { let x: i32 = -42; let y: u32 = x.abs() as u32; println!("The absolute value of {} is {}", x, y); + + let a: i32 = -3; + let _: usize = a.abs() as usize; + let _: usize = a.abs() as _; + let _ = a.abs() as usize; + + let a: i64 = -3; + let _ = a.abs() as usize; + let _ = a.abs() as u8; + let _ = a.abs() as u16; + let _ = a.abs() as u32; + let _ = a.abs() as u64; + let _ = a.abs() as u128; + + let a: isize = -3; + let _ = a.abs() as usize; + let _ = a.abs() as u8; + let _ = a.abs() as u16; + let _ = a.abs() as u32; + let _ = a.abs() as u64; + let _ = a.abs() as u128; } diff --git a/tests/ui/cast_abs_to_unsigned.stderr b/tests/ui/cast_abs_to_unsigned.stderr index eb12857357a44..02c24e10659ac 100644 --- a/tests/ui/cast_abs_to_unsigned.stderr +++ b/tests/ui/cast_abs_to_unsigned.stderr @@ -6,5 +6,95 @@ LL | let y: u32 = x.abs() as u32; | = note: `-D clippy::cast-abs-to-unsigned` implied by `-D warnings` -error: aborting due to previous error +error: casting the result of `i32::abs()` to usize + --> $DIR/cast_abs_to_unsigned.rs:10:20 + | +LL | let _: usize = a.abs() as usize; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `i32::abs()` to usize + --> $DIR/cast_abs_to_unsigned.rs:11:20 + | +LL | let _: usize = a.abs() as _; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `i32::abs()` to usize + --> $DIR/cast_abs_to_unsigned.rs:12:13 + | +LL | let _ = a.abs() as usize; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `i64::abs()` to usize + --> $DIR/cast_abs_to_unsigned.rs:15:13 + | +LL | let _ = a.abs() as usize; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `i64::abs()` to u8 + --> $DIR/cast_abs_to_unsigned.rs:16:13 + | +LL | let _ = a.abs() as u8; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `i64::abs()` to u16 + --> $DIR/cast_abs_to_unsigned.rs:17:13 + | +LL | let _ = a.abs() as u16; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `i64::abs()` to u32 + --> $DIR/cast_abs_to_unsigned.rs:18:13 + | +LL | let _ = a.abs() as u32; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `i64::abs()` to u64 + --> $DIR/cast_abs_to_unsigned.rs:19:13 + | +LL | let _ = a.abs() as u64; + | ^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `i64::abs()` to u128 + --> $DIR/cast_abs_to_unsigned.rs:20:13 + | +LL | let _ = a.abs() as u128; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `isize::abs()` to usize + --> $DIR/cast_abs_to_unsigned.rs:23:13 + | +LL | let _ = a.abs() as usize; + | ^^^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `isize::abs()` to u8 + --> $DIR/cast_abs_to_unsigned.rs:24:13 + | +LL | let _ = a.abs() as u8; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `isize::abs()` to u16 + --> $DIR/cast_abs_to_unsigned.rs:25:13 + | +LL | let _ = a.abs() as u16; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `isize::abs()` to u32 + --> $DIR/cast_abs_to_unsigned.rs:26:13 + | +LL | let _ = a.abs() as u32; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `isize::abs()` to u64 + --> $DIR/cast_abs_to_unsigned.rs:27:13 + | +LL | let _ = a.abs() as u64; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `isize::abs()` to u128 + --> $DIR/cast_abs_to_unsigned.rs:28:13 + | +LL | let _ = a.abs() as u128; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: aborting due to 16 previous errors From 0ad3a0ac07fd08fe6f6d6c603331f19c8d9b3033 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 23 May 2022 08:48:17 -0700 Subject: [PATCH 16/79] Lifetime variance fixes for clippy --- clippy_lints/src/casts/unnecessary_cast.rs | 12 ++++++------ clippy_lints/src/dereference.rs | 10 +++++----- clippy_lints/src/len_zero.rs | 12 ++++++------ clippy_lints/src/methods/mod.rs | 2 +- clippy_lints/src/ptr.rs | 6 +++--- .../src/transmute/transmute_undefined_repr.rs | 2 +- clippy_utils/src/ty.rs | 4 ++-- 7 files changed, 24 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index af56ec11ef8ac..fff7da8e33f2f 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -12,12 +12,12 @@ use rustc_middle::ty::{self, FloatTy, InferTy, Ty}; use super::UNNECESSARY_CAST; -pub(super) fn check( - cx: &LateContext<'_>, - expr: &Expr<'_>, - cast_expr: &Expr<'_>, - cast_from: Ty<'_>, - cast_to: Ty<'_>, +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &Expr<'tcx>, + cast_expr: &Expr<'tcx>, + cast_from: Ty<'tcx>, + cast_to: Ty<'tcx>, ) -> bool { // skip non-primitive type cast if_chain! { diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index ea4c0207bb013..8288f7a8b9b62 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -446,7 +446,7 @@ fn try_parse_ref_op<'tcx>( // Checks whether the type for a deref call actually changed the type, not just the mutability of // the reference. -fn deref_method_same_type(result_ty: Ty<'_>, arg_ty: Ty<'_>) -> bool { +fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool { match (result_ty.kind(), arg_ty.kind()) { (ty::Ref(_, result_ty, _), ty::Ref(_, arg_ty, _)) => result_ty == arg_ty, @@ -541,8 +541,8 @@ fn is_auto_borrow_position(parent: Option>, child_id: HirId) -> bool { /// Adjustments are sometimes made in the parent block rather than the expression itself. fn find_adjustments<'tcx>( tcx: TyCtxt<'tcx>, - typeck: &'tcx TypeckResults<'_>, - expr: &'tcx Expr<'_>, + typeck: &'tcx TypeckResults<'tcx>, + expr: &'tcx Expr<'tcx>, ) -> &'tcx [Adjustment<'tcx>] { let map = tcx.hir(); let mut iter = map.parent_iter(expr.hir_id); @@ -581,7 +581,7 @@ fn find_adjustments<'tcx>( } #[expect(clippy::needless_pass_by_value)] -fn report(cx: &LateContext<'_>, expr: &Expr<'_>, state: State, data: StateData) { +fn report<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, state: State, data: StateData) { match state { State::DerefMethod { ty_changed_count, @@ -656,7 +656,7 @@ fn report(cx: &LateContext<'_>, expr: &Expr<'_>, state: State, data: StateData) } impl Dereferencing { - fn check_local_usage(&mut self, cx: &LateContext<'_>, e: &Expr<'_>, local: HirId) { + fn check_local_usage<'tcx>(&mut self, cx: &LateContext<'tcx>, e: &Expr<'tcx>, local: HirId) { if let Some(outer_pat) = self.ref_locals.get_mut(&local) { if let Some(pat) = outer_pat { // Check for auto-deref diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index dabbb8375f0a6..246f5aad8fbad 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -259,8 +259,8 @@ fn parse_len_output<'tcx>(cx: &LateContext<'_>, sig: FnSig<'tcx>) -> Option { - fn matches_is_empty_output(self, ty: Ty<'_>) -> bool { +impl<'tcx> LenOutput<'tcx> { + fn matches_is_empty_output(self, ty: Ty<'tcx>) -> bool { match (self, ty.kind()) { (_, &ty::Bool) => true, (Self::Option(id), &ty::Adt(adt, subs)) if id == adt.did() => subs.type_at(0).is_bool(), @@ -292,7 +292,7 @@ impl LenOutput<'_> { } /// Checks if the given signature matches the expectations for `is_empty` -fn check_is_empty_sig(sig: FnSig<'_>, self_kind: ImplicitSelfKind, len_output: LenOutput<'_>) -> bool { +fn check_is_empty_sig<'tcx>(sig: FnSig<'tcx>, self_kind: ImplicitSelfKind, len_output: LenOutput<'tcx>) -> bool { match &**sig.inputs_and_output { [arg, res] if len_output.matches_is_empty_output(*res) => { matches!( @@ -306,11 +306,11 @@ fn check_is_empty_sig(sig: FnSig<'_>, self_kind: ImplicitSelfKind, len_output: L } /// Checks if the given type has an `is_empty` method with the appropriate signature. -fn check_for_is_empty( - cx: &LateContext<'_>, +fn check_for_is_empty<'tcx>( + cx: &LateContext<'tcx>, span: Span, self_kind: ImplicitSelfKind, - output: LenOutput<'_>, + output: LenOutput<'tcx>, impl_ty: DefId, item_name: Symbol, item_kind: &str, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 35fc452ed7cf0..3bf48e18019df 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2843,7 +2843,7 @@ enum SelfKind { impl SelfKind { fn matches<'a>(self, cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { - fn matches_value<'a>(cx: &LateContext<'a>, parent_ty: Ty<'_>, ty: Ty<'_>) -> bool { + fn matches_value<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { if ty == parent_ty { true } else if ty.is_box() { diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 86460c1b27e39..548f7b2528b11 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -395,9 +395,9 @@ impl<'tcx> DerefTy<'tcx> { fn check_fn_args<'cx, 'tcx: 'cx>( cx: &'cx LateContext<'tcx>, - tys: &'tcx [Ty<'_>], - hir_tys: &'tcx [hir::Ty<'_>], - params: &'tcx [Param<'_>], + tys: &'tcx [Ty<'tcx>], + hir_tys: &'tcx [hir::Ty<'tcx>], + params: &'tcx [Param<'tcx>], ) -> impl Iterator> + 'cx { tys.iter() .zip(hir_tys.iter()) diff --git a/clippy_lints/src/transmute/transmute_undefined_repr.rs b/clippy_lints/src/transmute/transmute_undefined_repr.rs index be6277332db4d..20b348fc14f7b 100644 --- a/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -358,7 +358,7 @@ fn is_size_pair(ty: Ty<'_>) -> bool { } } -fn same_except_params(subs1: SubstsRef<'_>, subs2: SubstsRef<'_>) -> bool { +fn same_except_params<'tcx>(subs1: SubstsRef<'tcx>, subs2: SubstsRef<'tcx>) -> bool { // TODO: check const parameters as well. Currently this will consider `Array<5>` the same as // `Array<6>` for (ty1, ty2) in subs1.types().zip(subs2.types()).filter(|(ty1, ty2)| ty1 != ty2) { diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 07d3d2807634f..75d27d3b59482 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -42,7 +42,7 @@ pub fn can_partially_move_ty<'tcx>(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 { +pub fn contains_ty<'tcx>(ty: Ty<'tcx>, other_ty: Ty<'tcx>) -> bool { ty.walk().any(|inner| match inner.unpack() { GenericArgKind::Type(inner_ty) => other_ty == inner_ty, GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, @@ -51,7 +51,7 @@ 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 { +pub fn contains_adt_constructor<'tcx>(ty: Ty<'tcx>, adt: AdtDef<'tcx>) -> bool { ty.walk().any(|inner| match inner.unpack() { GenericArgKind::Type(inner_ty) => inner_ty.ty_adt_def() == Some(adt), GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, From 0684d62b564c2e88bd812fccf00d35bcd2554cb6 Mon Sep 17 00:00:00 2001 From: Jakob Degen Date: Sat, 16 Apr 2022 09:27:54 -0400 Subject: [PATCH 17/79] Refactor call terminator to always hold a destination place --- clippy_lints/src/redundant_clone.rs | 4 ++-- clippy_utils/src/qualify_min_const_fn.rs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 0004b8afdd375..ab16fe47d4dfb 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -292,7 +292,7 @@ fn is_call_with_ref_arg<'tcx>( if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(&*mir, cx.tcx)); if !is_copy(cx, inner_ty); then { - Some((def_id, *local, inner_ty, destination.as_ref().map(|(dest, _)| dest)?.as_local()?)) + Some((def_id, *local, inner_ty, destination.as_local()?)) } else { None } @@ -584,7 +584,7 @@ impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> { fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Location) { if let mir::TerminatorKind::Call { args, - destination: Some((mir::Place { local: dest, .. }, _)), + destination: mir::Place { local: dest, .. }, .. } = &terminator.kind { diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index a6d7042fabc26..78d8f1e213af0 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -301,6 +301,7 @@ fn check_terminator<'a, 'tcx>( args, from_hir_call: _, destination: _, + target: _, cleanup: _, fn_span: _, } => { From ea62347a5a4b952b416dddc8053a495c9238e7d7 Mon Sep 17 00:00:00 2001 From: Federico Guerinoni Date: Mon, 25 Apr 2022 16:12:48 +0200 Subject: [PATCH 18/79] New lint `no_effect_replace` Closes #1595 changelog: Add no_effect_replace lint. --- CHANGELOG.md | 1 + clippy_lints/src/lib.register_all.rs | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_suspicious.rs | 1 + clippy_lints/src/methods/mod.rs | 23 ++++++++ clippy_lints/src/methods/no_effect_replace.rs | 47 +++++++++++++++++ tests/ui/no_effect_replace.rs | 51 ++++++++++++++++++ tests/ui/no_effect_replace.stderr | 52 +++++++++++++++++++ 8 files changed, 177 insertions(+) create mode 100644 clippy_lints/src/methods/no_effect_replace.rs create mode 100644 tests/ui/no_effect_replace.rs create mode 100644 tests/ui/no_effect_replace.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 47aaae9c1986b..4b62ccbf8bcb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3594,6 +3594,7 @@ Released 2018-09-13 [`new_ret_no_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_ret_no_self [`new_without_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default [`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect +[`no_effect_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_replace [`no_effect_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_underscore_binding [`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal [`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index fca5e109509d4..2d0dca87b1088 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -190,6 +190,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(methods::NEEDLESS_OPTION_TAKE), LintId::of(methods::NEEDLESS_SPLITN), LintId::of(methods::NEW_RET_NO_SELF), + LintId::of(methods::NO_EFFECT_REPLACE), LintId::of(methods::OK_EXPECT), LintId::of(methods::OPTION_AS_REF_DEREF), LintId::of(methods::OPTION_FILTER_MAP), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 7489f53fd3c37..5d6360838ec6e 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -331,6 +331,7 @@ store.register_lints(&[ methods::NEEDLESS_OPTION_TAKE, methods::NEEDLESS_SPLITN, methods::NEW_RET_NO_SELF, + methods::NO_EFFECT_REPLACE, methods::OK_EXPECT, methods::OPTION_AS_REF_DEREF, methods::OPTION_FILTER_MAP, diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs index 940bf7ace5e83..7e1ba5336c677 100644 --- a/clippy_lints/src/lib.register_suspicious.rs +++ b/clippy_lints/src/lib.register_suspicious.rs @@ -23,6 +23,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(loops::EMPTY_LOOP), LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES), LintId::of(loops::MUT_RANGE_BOUND), + LintId::of(methods::NO_EFFECT_REPLACE), LintId::of(methods::SUSPICIOUS_MAP), LintId::of(mut_key::MUTABLE_KEY_TYPE), LintId::of(octal_escapes::OCTAL_ESCAPES), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index cfeee4d0c7091..5102fa17a67e5 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -44,6 +44,7 @@ mod map_identity; mod map_unwrap_or; mod needless_option_as_deref; mod needless_option_take; +mod no_effect_replace; mod ok_expect; mod option_as_ref_deref; mod option_map_or_none; @@ -2197,6 +2198,24 @@ declare_clippy_lint! { "using `.as_ref().take()` on a temporary value" } +declare_clippy_lint! { + /// ### What it does + /// Checks for `replace` statements which have no effect. + /// + /// ### Why is this bad? + /// It's either a mistake or confusing. + /// + /// ### Example + /// ```rust + /// "1234".replace("12", "12"); + /// "1234".replacen("12", "12", 1); + /// ``` + #[clippy::version = "1.62.0"] + pub NO_EFFECT_REPLACE, + suspicious, + "replace with no effect" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Option, @@ -2296,6 +2315,7 @@ impl_lint_pass!(Methods => [ NEEDLESS_OPTION_AS_DEREF, IS_DIGIT_ASCII_RADIX, NEEDLESS_OPTION_TAKE, + NO_EFFECT_REPLACE, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -2707,6 +2727,9 @@ impl Methods { unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or"); }, }, + ("replace" | "replacen", [arg1, arg2] | [arg1, arg2, _]) => { + no_effect_replace::check(cx, expr, arg1, arg2); + }, _ => {}, } } diff --git a/clippy_lints/src/methods/no_effect_replace.rs b/clippy_lints/src/methods/no_effect_replace.rs new file mode 100644 index 0000000000000..a76341855b6db --- /dev/null +++ b/clippy_lints/src/methods/no_effect_replace.rs @@ -0,0 +1,47 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::SpanlessEq; +use if_chain::if_chain; +use rustc_ast::LitKind; +use rustc_hir::ExprKind; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::NO_EFFECT_REPLACE; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx rustc_hir::Expr<'_>, + arg1: &'tcx rustc_hir::Expr<'_>, + arg2: &'tcx rustc_hir::Expr<'_>, +) { + let ty = cx.typeck_results().expr_ty(expr).peel_refs(); + if !(ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String)) { + return; + } + + if_chain! { + if let ExprKind::Lit(spanned) = &arg1.kind; + if let Some(param1) = lit_string_value(&spanned.node); + + if let ExprKind::Lit(spanned) = &arg2.kind; + if let LitKind::Str(param2, _) = &spanned.node; + if param1 == param2.as_str(); + + then { + span_lint(cx, NO_EFFECT_REPLACE, expr.span, "replacing text with itself"); + } + } + + if SpanlessEq::new(cx).eq_expr(arg1, arg2) { + span_lint(cx, NO_EFFECT_REPLACE, expr.span, "replacing text with itself"); + } +} + +fn lit_string_value(node: &LitKind) -> Option { + match node { + LitKind::Char(value) => Some(value.to_string()), + LitKind::Str(value, _) => Some(value.as_str().to_owned()), + _ => None, + } +} diff --git a/tests/ui/no_effect_replace.rs b/tests/ui/no_effect_replace.rs new file mode 100644 index 0000000000000..ad17d53f78906 --- /dev/null +++ b/tests/ui/no_effect_replace.rs @@ -0,0 +1,51 @@ +#![warn(clippy::no_effect_replace)] + +fn main() { + let _ = "12345".replace('1', "1"); + let _ = "12345".replace("12", "12"); + let _ = String::new().replace("12", "12"); + + let _ = "12345".replacen('1', "1", 1); + let _ = "12345".replacen("12", "12", 1); + let _ = String::new().replacen("12", "12", 1); + + let _ = "12345".replace("12", "22"); + let _ = "12345".replacen("12", "22", 1); + + let mut x = X::default(); + let _ = "hello".replace(&x.f(), &x.f()); + let _ = "hello".replace(&x.f(), &x.ff()); + + let _ = "hello".replace(&y(), &y()); + let _ = "hello".replace(&y(), &z()); + + let _ = Replaceme.replace("a", "a"); +} + +#[derive(Default)] +struct X {} + +impl X { + fn f(&mut self) -> String { + "he".to_string() + } + + fn ff(&mut self) -> String { + "hh".to_string() + } +} + +fn y() -> String { + "he".to_string() +} + +fn z() -> String { + "hh".to_string() +} + +struct Replaceme; +impl Replaceme { + pub fn replace(&mut self, a: &str, b: &str) -> Self { + Self + } +} diff --git a/tests/ui/no_effect_replace.stderr b/tests/ui/no_effect_replace.stderr new file mode 100644 index 0000000000000..53a28aa73b707 --- /dev/null +++ b/tests/ui/no_effect_replace.stderr @@ -0,0 +1,52 @@ +error: replacing text with itself + --> $DIR/no_effect_replace.rs:4:13 + | +LL | let _ = "12345".replace('1', "1"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::no-effect-replace` implied by `-D warnings` + +error: replacing text with itself + --> $DIR/no_effect_replace.rs:5:13 + | +LL | let _ = "12345".replace("12", "12"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: replacing text with itself + --> $DIR/no_effect_replace.rs:6:13 + | +LL | let _ = String::new().replace("12", "12"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: replacing text with itself + --> $DIR/no_effect_replace.rs:8:13 + | +LL | let _ = "12345".replacen('1', "1", 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: replacing text with itself + --> $DIR/no_effect_replace.rs:9:13 + | +LL | let _ = "12345".replacen("12", "12", 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: replacing text with itself + --> $DIR/no_effect_replace.rs:10:13 + | +LL | let _ = String::new().replacen("12", "12", 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: replacing text with itself + --> $DIR/no_effect_replace.rs:16:13 + | +LL | let _ = "hello".replace(&x.f(), &x.f()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: replacing text with itself + --> $DIR/no_effect_replace.rs:19:13 + | +LL | let _ = "hello".replace(&y(), &y()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 8 previous errors + From 22673bce4cd8bad876d047172680a9ced02ba4c1 Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Tue, 24 May 2022 14:41:23 +0200 Subject: [PATCH 19/79] add testcase for #8878 --- tests/ui/map_flatten_fixable.fixed | 18 ++++++++++++++++++ tests/ui/map_flatten_fixable.rs | 17 +++++++++++++++++ tests/ui/map_flatten_fixable.stderr | 28 +++++++++++++++++++++++++++- 3 files changed, 62 insertions(+), 1 deletion(-) diff --git a/tests/ui/map_flatten_fixable.fixed b/tests/ui/map_flatten_fixable.fixed index fec3a95edd62d..08aba35e14db8 100644 --- a/tests/ui/map_flatten_fixable.fixed +++ b/tests/ui/map_flatten_fixable.fixed @@ -28,4 +28,22 @@ fn main() { // mapping to Result on Result let _: Result<_, &str> = (Ok(Ok(1))).and_then(|x| x); + + issue8878(); +} + +#[allow(clippy::bind_instead_of_map)] // map + flatten will be suggested to `and_then`, but afterwards `map` is suggested again +#[rustfmt::skip] // whitespace is important for this one +fn issue8878() { + std::collections::HashMap::::new() + .get(&0) + .and_then(|_| { +// we need some newlines +// so that the span is big enough +// we need some newlines +// so that the span is big enough +// for a splitted output of the diagnostic + Some("") + // whitespace beforehand is important as well + }); } diff --git a/tests/ui/map_flatten_fixable.rs b/tests/ui/map_flatten_fixable.rs index aa1f76e335af0..fbe2bf8a37d78 100644 --- a/tests/ui/map_flatten_fixable.rs +++ b/tests/ui/map_flatten_fixable.rs @@ -28,4 +28,21 @@ fn main() { // mapping to Result on Result let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten(); + + issue8878(); +} + +#[allow(clippy::bind_instead_of_map)] // map + flatten will be suggested to `and_then`, but afterwards `map` is suggested again +#[rustfmt::skip] // whitespace is important for this one +fn issue8878() { + std::collections::HashMap::::new() + .get(&0) + .map(|_| { +// we need some newlines +// so that the span is big enough +// for a splitted output of the diagnostic + Some("") + // whitespace beforehand is important as well + }) + .flatten(); } diff --git a/tests/ui/map_flatten_fixable.stderr b/tests/ui/map_flatten_fixable.stderr index c91c73846b69f..5ee2afca1d001 100644 --- a/tests/ui/map_flatten_fixable.stderr +++ b/tests/ui/map_flatten_fixable.stderr @@ -76,5 +76,31 @@ help: try replacing `map` with `and_then`, and remove the `.flatten()` LL | let _: Result<_, &str> = (Ok(Ok(1))).and_then(|x| x); | ~~~~~~~~~~~~~~~ -error: aborting due to 7 previous errors +error: called `map(..).flatten()` on `Option` + --> $DIR/map_flatten_fixable.rs:40:10 + | +LL | .map(|_| { + | __________^ +LL | | // we need some newlines +LL | | // so that the span is big enough +LL | | // for a splitted output of the diagnostic +... | +LL | | }) +LL | | .flatten(); + | |__________________^ + | +help: try replacing `map` with `and_then` + | +LL ~ .and_then(|_| { +LL + // we need some newlines +LL + // so that the span is big enough + | +help: and remove the `.flatten()` + | +LL + Some("") +LL + // whitespace beforehand is important as well +LL ~ }); + | + +error: aborting due to 8 previous errors From 04297de2ed5f9e822fc4bf79e24556a68df56dce Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Tue, 24 May 2022 14:42:08 +0200 Subject: [PATCH 20/79] prepare test for 8734 --- tests/ui/map_flatten_fixable.fixed | 19 +++++++++++++++++++ tests/ui/map_flatten_fixable.rs | 19 +++++++++++++++++++ tests/ui/map_flatten_fixable.stderr | 2 +- 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/tests/ui/map_flatten_fixable.fixed b/tests/ui/map_flatten_fixable.fixed index 08aba35e14db8..928e5bd509c3f 100644 --- a/tests/ui/map_flatten_fixable.fixed +++ b/tests/ui/map_flatten_fixable.fixed @@ -29,9 +29,28 @@ fn main() { // mapping to Result on Result let _: Result<_, &str> = (Ok(Ok(1))).and_then(|x| x); + issue8734(); issue8878(); } +fn issue8734() { + // let _ = [0u8, 1, 2, 3] + // .into_iter() + // .map(|n| match n { + // 1 => [n + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1)], + // n => [n], + // }) + // .flatten(); +} + #[allow(clippy::bind_instead_of_map)] // map + flatten will be suggested to `and_then`, but afterwards `map` is suggested again #[rustfmt::skip] // whitespace is important for this one fn issue8878() { diff --git a/tests/ui/map_flatten_fixable.rs b/tests/ui/map_flatten_fixable.rs index fbe2bf8a37d78..4345c6eee7461 100644 --- a/tests/ui/map_flatten_fixable.rs +++ b/tests/ui/map_flatten_fixable.rs @@ -29,9 +29,28 @@ fn main() { // mapping to Result on Result let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten(); + issue8734(); issue8878(); } +fn issue8734() { + // let _ = [0u8, 1, 2, 3] + // .into_iter() + // .map(|n| match n { + // 1 => [n + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1)], + // n => [n], + // }) + // .flatten(); +} + #[allow(clippy::bind_instead_of_map)] // map + flatten will be suggested to `and_then`, but afterwards `map` is suggested again #[rustfmt::skip] // whitespace is important for this one fn issue8878() { diff --git a/tests/ui/map_flatten_fixable.stderr b/tests/ui/map_flatten_fixable.stderr index 5ee2afca1d001..828e24acaad6c 100644 --- a/tests/ui/map_flatten_fixable.stderr +++ b/tests/ui/map_flatten_fixable.stderr @@ -77,7 +77,7 @@ LL | let _: Result<_, &str> = (Ok(Ok(1))).and_then(|x| x); | ~~~~~~~~~~~~~~~ error: called `map(..).flatten()` on `Option` - --> $DIR/map_flatten_fixable.rs:40:10 + --> $DIR/map_flatten_fixable.rs:59:10 | LL | .map(|_| { | __________^ From 21d949381c282355cc949eea70a9b1617315bdd3 Mon Sep 17 00:00:00 2001 From: Dany Marcoux Date: Tue, 24 May 2022 14:53:08 +0200 Subject: [PATCH 21/79] Fix imports for "Checking if a type defines a specific method" The import of `clippy_utils::is_type_diagnostic_item` would cause this error: ``` error[E0432]: unresolved import `clippy_utils::is_type_diagnostic_item ``` --- doc/common_tools_writing_lints.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/common_tools_writing_lints.md b/doc/common_tools_writing_lints.md index 131ac3c3611e8..1d1aee0da2cc7 100644 --- a/doc/common_tools_writing_lints.md +++ b/doc/common_tools_writing_lints.md @@ -159,7 +159,8 @@ A list of defined paths for Clippy can be found in [paths.rs][paths] To check if our type defines a method called `some_method`: ```rust -use clippy_utils::{is_type_diagnostic_item, return_ty}; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::return_ty; impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { From 78f7e3745fe59db1e6309b1e42ea25df3317ee36 Mon Sep 17 00:00:00 2001 From: Evan Typanski Date: Tue, 24 May 2022 11:50:12 -0400 Subject: [PATCH 22/79] Fix `manual_range_contains` with equal precedence --- clippy_lints/src/ranges.rs | 13 +++++++++++-- tests/ui/range_contains.fixed | 5 +++++ tests/ui/range_contains.rs | 5 +++++ tests/ui/range_contains.stderr | 26 +++++++++++++++++++++++++- 4 files changed, 46 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index a47dc26f60347..416d6de55c8b5 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -194,7 +194,7 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { }, ExprKind::Binary(ref op, l, r) => { if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) { - check_possible_range_contains(cx, op.node, l, r, expr); + check_possible_range_contains(cx, op.node, l, r, expr, expr.span); } }, _ => {}, @@ -213,12 +213,12 @@ fn check_possible_range_contains( left: &Expr<'_>, right: &Expr<'_>, expr: &Expr<'_>, + span: Span, ) { if in_constant(cx, expr.hir_id) { return; } - let span = expr.span; let combine_and = match op { BinOpKind::And | BinOpKind::BitAnd => true, BinOpKind::Or | BinOpKind::BitOr => false, @@ -294,6 +294,15 @@ fn check_possible_range_contains( ); } } + + // If the LHS is the same operator, we have to recurse to get the "real" RHS, since they have + // the same operator precedence. + if let ExprKind::Binary(ref lhs_op, _left, new_lhs) = left.kind { + if op == lhs_op.node { + let new_span = Span::new(new_lhs.span.lo(), right.span.hi(), expr.span.ctxt(), expr.span.parent()); + check_possible_range_contains(cx, op, new_lhs, right, expr, new_span); + } + } } struct RangeBounds<'a> { diff --git a/tests/ui/range_contains.fixed b/tests/ui/range_contains.fixed index f497719971174..fd4dcf39f2f25 100644 --- a/tests/ui/range_contains.fixed +++ b/tests/ui/range_contains.fixed @@ -49,6 +49,11 @@ fn main() { x >= 10 && x <= -10; (-3. ..=3.).contains(&y); y >= 3. && y <= -3.; + + // Fix #8745 + let z = 42; + (0..=10).contains(&x) && (0..=10).contains(&z); + !(0..10).contains(&x) || !(0..10).contains(&z); } // Fix #6373 diff --git a/tests/ui/range_contains.rs b/tests/ui/range_contains.rs index 9e2180b0c9944..b848e2e167122 100644 --- a/tests/ui/range_contains.rs +++ b/tests/ui/range_contains.rs @@ -49,6 +49,11 @@ fn main() { x >= 10 && x <= -10; y >= -3. && y <= 3.; y >= 3. && y <= -3.; + + // Fix #8745 + let z = 42; + (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10); + (x < 0) || (x >= 10) || (z < 0) || (z >= 10); } // Fix #6373 diff --git a/tests/ui/range_contains.stderr b/tests/ui/range_contains.stderr index 1817ee1715d17..936859db5a126 100644 --- a/tests/ui/range_contains.stderr +++ b/tests/ui/range_contains.stderr @@ -96,5 +96,29 @@ error: manual `RangeInclusive::contains` implementation LL | y >= -3. && y <= 3.; | ^^^^^^^^^^^^^^^^^^^ help: use: `(-3. ..=3.).contains(&y)` -error: aborting due to 16 previous errors +error: manual `RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:55:30 + | +LL | (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10); + | ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&z)` + +error: manual `RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:55:5 + | +LL | (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10); + | ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&x)` + +error: manual `!Range::contains` implementation + --> $DIR/range_contains.rs:56:29 + | +LL | (x < 0) || (x >= 10) || (z < 0) || (z >= 10); + | ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&z)` + +error: manual `!Range::contains` implementation + --> $DIR/range_contains.rs:56:5 + | +LL | (x < 0) || (x >= 10) || (z < 0) || (z >= 10); + | ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&x)` + +error: aborting due to 20 previous errors From 257f09776a9c68ac903d1f23b80c414b70de1185 Mon Sep 17 00:00:00 2001 From: Evan Typanski Date: Tue, 24 May 2022 14:09:56 -0400 Subject: [PATCH 23/79] Fix issue with mismatched parens in suggestion --- clippy_lints/src/ranges.rs | 13 +++++++++---- tests/ui/range_contains.fixed | 2 ++ tests/ui/range_contains.rs | 2 ++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 416d6de55c8b5..584b561dcf0ba 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -296,10 +296,15 @@ fn check_possible_range_contains( } // If the LHS is the same operator, we have to recurse to get the "real" RHS, since they have - // the same operator precedence. - if let ExprKind::Binary(ref lhs_op, _left, new_lhs) = left.kind { - if op == lhs_op.node { - let new_span = Span::new(new_lhs.span.lo(), right.span.hi(), expr.span.ctxt(), expr.span.parent()); + // the same operator precedence + if_chain! { + if let ExprKind::Binary(ref lhs_op, _left, new_lhs) = left.kind; + if op == lhs_op.node; + let new_span = Span::new(new_lhs.span.lo(), right.span.hi(), expr.span.ctxt(), expr.span.parent()); + if let Some(snip) = &snippet_opt(cx, new_span); + // Do not continue if we have mismatched number of parens, otherwise the suggestion is wrong + if snip.matches('(').count() == snip.matches(')').count(); + then { check_possible_range_contains(cx, op, new_lhs, right, expr, new_span); } } diff --git a/tests/ui/range_contains.fixed b/tests/ui/range_contains.fixed index fd4dcf39f2f25..85d021b2f25e2 100644 --- a/tests/ui/range_contains.fixed +++ b/tests/ui/range_contains.fixed @@ -54,6 +54,8 @@ fn main() { let z = 42; (0..=10).contains(&x) && (0..=10).contains(&z); !(0..10).contains(&x) || !(0..10).contains(&z); + // Make sure operators in parens don't give a breaking suggestion + ((x % 2 == 0) || (x < 0)) || (x >= 10); } // Fix #6373 diff --git a/tests/ui/range_contains.rs b/tests/ui/range_contains.rs index b848e2e167122..9a7a75dc13254 100644 --- a/tests/ui/range_contains.rs +++ b/tests/ui/range_contains.rs @@ -54,6 +54,8 @@ fn main() { let z = 42; (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10); (x < 0) || (x >= 10) || (z < 0) || (z >= 10); + // Make sure operators in parens don't give a breaking suggestion + ((x % 2 == 0) || (x < 0)) || (x >= 10); } // Fix #6373 From 1dd026698d4f6c5848fb88d48b2d4248846bb41c Mon Sep 17 00:00:00 2001 From: kyoto7250 <50972773+kyoto7250@users.noreply.github.com> Date: Tue, 24 May 2022 18:54:49 +0900 Subject: [PATCH 24/79] feat(lint): impl lint about use first() instead of get(0) --- CHANGELOG.md | 1 + clippy_lints/src/get_first.rs | 72 ++++++++++++++++++++++++++ clippy_lints/src/lib.register_all.rs | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_style.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_utils/src/paths.rs | 1 + tests/ui/get_first.fixed | 42 +++++++++++++++ tests/ui/get_first.rs | 42 +++++++++++++++ tests/ui/get_first.stderr | 22 ++++++++ 10 files changed, 185 insertions(+) create mode 100644 clippy_lints/src/get_first.rs create mode 100644 tests/ui/get_first.fixed create mode 100644 tests/ui/get_first.rs create mode 100644 tests/ui/get_first.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index f4a362e6c198c..0f6295a45f3fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3435,6 +3435,7 @@ Released 2018-09-13 [`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into [`from_str_radix_10`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_str_radix_10 [`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send +[`get_first`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_first [`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len [`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap [`identity_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_conversion diff --git a/clippy_lints/src/get_first.rs b/clippy_lints/src/get_first.rs new file mode 100644 index 0000000000000..0f26c19c49eec --- /dev/null +++ b/clippy_lints/src/get_first.rs @@ -0,0 +1,72 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{is_slice_of_primitives, match_def_path, paths}; +use if_chain::if_chain; +use rustc_ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Spanned; + +declare_clippy_lint! { + /// ### What it does + /// Checks for using `x.get(0)` instead of + /// `x.first()`. + /// + /// ### Why is this bad? + /// Using `x.first()` is easier to read and has the same + /// result. + /// + /// ### Example + /// ```rust + /// // Bad + /// let x = vec![2, 3, 5]; + /// let first_element = x.get(0); + /// ``` + /// Use instead: + /// ```rust + /// // Good + /// let x = vec![2, 3, 5]; + /// let first_element = x.first(); + /// ``` + #[clippy::version = "1.63.0"] + pub GET_FIRST, + style, + "Using `x.get(0)` when `x.first()` is simpler" +} +declare_lint_pass!(GetFirst => [GET_FIRST]); + +impl<'tcx> LateLintPass<'tcx> for GetFirst { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if_chain! { + if let hir::ExprKind::MethodCall(_, expr_args, _) = &expr.kind; + if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if match_def_path(cx, expr_def_id, &paths::SLICE_GET) && expr_args.len() == 2; + + if let Some(struct_calling_on) = expr_args.get(0); + if let Some(_) = is_slice_of_primitives(cx, struct_calling_on); + + if let Some(method_arg) = expr_args.get(1); + if let hir::ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = method_arg.kind; + + then { + let mut applicability = Applicability::MachineApplicable; + let slice_name = snippet_with_applicability( + cx, + struct_calling_on.span, "..", + &mut applicability, + ); + span_lint_and_sugg( + cx, + GET_FIRST, + expr.span, + &format!("accessing first element with `{0}.get(0)`", slice_name), + "try", + format!("{}.first()", slice_name), + applicability, + ); + } + } + } +} diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 5aba6040b8c5a..a028b41db7740 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -91,6 +91,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF), LintId::of(functions::RESULT_UNIT_ERR), LintId::of(functions::TOO_MANY_ARGUMENTS), + LintId::of(get_first::GET_FIRST), LintId::of(identity_op::IDENTITY_OP), LintId::of(if_let_mutex::IF_LET_MUTEX), LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index fea34fe66f4e9..570d736518bde 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -183,6 +183,7 @@ store.register_lints(&[ functions::TOO_MANY_ARGUMENTS, functions::TOO_MANY_LINES, future_not_send::FUTURE_NOT_SEND, + get_first::GET_FIRST, identity_op::IDENTITY_OP, if_let_mutex::IF_LET_MUTEX, if_not_else::IF_NOT_ELSE, diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index 62f26d821a0d6..ea2e1082458c2 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -31,6 +31,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(functions::DOUBLE_MUST_USE), LintId::of(functions::MUST_USE_UNIT), LintId::of(functions::RESULT_UNIT_ERR), + LintId::of(get_first::GET_FIRST), LintId::of(inherent_to_string::INHERENT_TO_STRING), LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS), LintId::of(len_zero::COMPARISON_TO_EMPTY), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2f8533723efc5..5f636e5114bdd 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -242,6 +242,7 @@ mod from_over_into; mod from_str_radix_10; mod functions; mod future_not_send; +mod get_first; mod identity_op; mod if_let_mutex; mod if_not_else; @@ -904,6 +905,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(strings::TrimSplitWhitespace)); store.register_late_pass(|| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)); store.register_early_pass(|| Box::new(duplicate_mod::DuplicateMod::default())); + store.register_late_pass(|| Box::new(get_first::GetFirst)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 9b9cbff2d1462..0064694ff929f 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -141,6 +141,7 @@ pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"]; pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"]; pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"]; pub const SLICE_FROM_RAW_PARTS_MUT: [&str; 4] = ["core", "slice", "raw", "from_raw_parts_mut"]; +pub const SLICE_GET: [&str; 4] = ["core", "slice", "", "get"]; pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "", "into_vec"]; pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"]; pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"]; diff --git a/tests/ui/get_first.fixed b/tests/ui/get_first.fixed new file mode 100644 index 0000000000000..def58afa4fbf2 --- /dev/null +++ b/tests/ui/get_first.fixed @@ -0,0 +1,42 @@ +// run-rustfix +#![warn(clippy::get_first)] +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::collections::VecDeque; + +struct Bar { + arr: [u32; 3], +} + +impl Bar { + fn get(&self, pos: usize) -> Option<&u32> { + self.arr.get(pos) + } +} + +fn main() { + let x = vec![2, 3, 5]; + let _ = x.first(); // Use x.first() + let _ = x.get(1); + let _ = x[0]; + + let y = [2, 3, 5]; + let _ = y.first(); // Use y.first() + let _ = y.get(1); + let _ = y[0]; + + let z = &[2, 3, 5]; + let _ = z.first(); // Use z.first() + let _ = z.get(1); + let _ = z[0]; + + let vecdeque: VecDeque<_> = x.iter().cloned().collect(); + let hashmap: HashMap = HashMap::from_iter(vec![(0, 'a'), (1, 'b')]); + let btreemap: BTreeMap = BTreeMap::from_iter(vec![(0, 'a'), (1, 'b')]); + let _ = vecdeque.get(0); // Do not lint, because VecDeque is not slice. + let _ = hashmap.get(&0); // Do not lint, because HashMap is not slice. + let _ = btreemap.get(&0); // Do not lint, because BTreeMap is not slice. + + let bar = Bar { arr: [0, 1, 2] }; + let _ = bar.get(0); // Do not lint, because Bar is struct. +} diff --git a/tests/ui/get_first.rs b/tests/ui/get_first.rs new file mode 100644 index 0000000000000..85a381854cd3b --- /dev/null +++ b/tests/ui/get_first.rs @@ -0,0 +1,42 @@ +// run-rustfix +#![warn(clippy::get_first)] +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::collections::VecDeque; + +struct Bar { + arr: [u32; 3], +} + +impl Bar { + fn get(&self, pos: usize) -> Option<&u32> { + self.arr.get(pos) + } +} + +fn main() { + let x = vec![2, 3, 5]; + let _ = x.get(0); // Use x.first() + let _ = x.get(1); + let _ = x[0]; + + let y = [2, 3, 5]; + let _ = y.get(0); // Use y.first() + let _ = y.get(1); + let _ = y[0]; + + let z = &[2, 3, 5]; + let _ = z.get(0); // Use z.first() + let _ = z.get(1); + let _ = z[0]; + + let vecdeque: VecDeque<_> = x.iter().cloned().collect(); + let hashmap: HashMap = HashMap::from_iter(vec![(0, 'a'), (1, 'b')]); + let btreemap: BTreeMap = BTreeMap::from_iter(vec![(0, 'a'), (1, 'b')]); + let _ = vecdeque.get(0); // Do not lint, because VecDeque is not slice. + let _ = hashmap.get(&0); // Do not lint, because HashMap is not slice. + let _ = btreemap.get(&0); // Do not lint, because BTreeMap is not slice. + + let bar = Bar { arr: [0, 1, 2] }; + let _ = bar.get(0); // Do not lint, because Bar is struct. +} diff --git a/tests/ui/get_first.stderr b/tests/ui/get_first.stderr new file mode 100644 index 0000000000000..466beff9c92df --- /dev/null +++ b/tests/ui/get_first.stderr @@ -0,0 +1,22 @@ +error: accessing first element with `x.get(0)` + --> $DIR/get_first.rs:19:13 + | +LL | let _ = x.get(0); // Use x.first() + | ^^^^^^^^ help: try: `x.first()` + | + = note: `-D clippy::get-first` implied by `-D warnings` + +error: accessing first element with `y.get(0)` + --> $DIR/get_first.rs:24:13 + | +LL | let _ = y.get(0); // Use y.first() + | ^^^^^^^^ help: try: `y.first()` + +error: accessing first element with `z.get(0)` + --> $DIR/get_first.rs:29:13 + | +LL | let _ = z.get(0); // Use z.first() + | ^^^^^^^^ help: try: `z.first()` + +error: aborting due to 3 previous errors + From b531eb1a7aca3357d2d5fce97f3f07d2a6956bfe Mon Sep 17 00:00:00 2001 From: kyoto7250 <50972773+kyoto7250@users.noreply.github.com> Date: Tue, 24 May 2022 18:55:39 +0900 Subject: [PATCH 25/79] suggest first() instead of get(0) --- clippy_lints/src/methods/iter_next_slice.rs | 9 +++++++-- tests/ui/iter_next_slice.fixed | 8 ++++---- tests/ui/iter_next_slice.rs | 4 ++-- tests/ui/iter_next_slice.stderr | 4 ++-- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/methods/iter_next_slice.rs b/clippy_lints/src/methods/iter_next_slice.rs index d053ff567565c..b8d1dabe00764 100644 --- a/clippy_lints/src/methods/iter_next_slice.rs +++ b/clippy_lints/src/methods/iter_next_slice.rs @@ -34,13 +34,18 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal if let ast::LitKind::Int(start_idx, _) = start_lit.node; then { let mut applicability = Applicability::MachineApplicable; + let suggest = if start_idx == 0 { + format!("{}.first()", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability)) + } else { + format!("{}.get({})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability), start_idx) + }; span_lint_and_sugg( cx, ITER_NEXT_SLICE, expr.span, "using `.iter().next()` on a Slice without end index", "try calling", - format!("{}.get({})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability), start_idx), + suggest, applicability, ); } @@ -55,7 +60,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal "using `.iter().next()` on an array", "try calling", format!( - "{}.get(0)", + "{}.first()", snippet_with_applicability(cx, caller_expr.span, "..", &mut applicability) ), applicability, diff --git a/tests/ui/iter_next_slice.fixed b/tests/ui/iter_next_slice.fixed index 11ffc8edb149e..f612d26aaabcc 100644 --- a/tests/ui/iter_next_slice.fixed +++ b/tests/ui/iter_next_slice.fixed @@ -6,8 +6,8 @@ fn main() { let s = [1, 2, 3]; let v = vec![1, 2, 3]; - let _ = s.get(0); - // Should be replaced by s.get(0) + let _ = s.first(); + // Should be replaced by s.first() let _ = s.get(2); // Should be replaced by s.get(2) @@ -15,8 +15,8 @@ fn main() { let _ = v.get(5); // Should be replaced by v.get(5) - let _ = v.get(0); - // Should be replaced by v.get(0) + let _ = v.first(); + // Should be replaced by v.first() let o = Some(5); o.iter().next(); diff --git a/tests/ui/iter_next_slice.rs b/tests/ui/iter_next_slice.rs index e0d3aabd54acd..5195f1c86675f 100644 --- a/tests/ui/iter_next_slice.rs +++ b/tests/ui/iter_next_slice.rs @@ -7,7 +7,7 @@ fn main() { let v = vec![1, 2, 3]; let _ = s.iter().next(); - // Should be replaced by s.get(0) + // Should be replaced by s.first() let _ = s[2..].iter().next(); // Should be replaced by s.get(2) @@ -16,7 +16,7 @@ fn main() { // Should be replaced by v.get(5) let _ = v.iter().next(); - // Should be replaced by v.get(0) + // Should be replaced by v.first() let o = Some(5); o.iter().next(); diff --git a/tests/ui/iter_next_slice.stderr b/tests/ui/iter_next_slice.stderr index a78d2c2d5e838..d8b89061ff895 100644 --- a/tests/ui/iter_next_slice.stderr +++ b/tests/ui/iter_next_slice.stderr @@ -2,7 +2,7 @@ error: using `.iter().next()` on an array --> $DIR/iter_next_slice.rs:9:13 | LL | let _ = s.iter().next(); - | ^^^^^^^^^^^^^^^ help: try calling: `s.get(0)` + | ^^^^^^^^^^^^^^^ help: try calling: `s.first()` | = note: `-D clippy::iter-next-slice` implied by `-D warnings` @@ -22,7 +22,7 @@ error: using `.iter().next()` on an array --> $DIR/iter_next_slice.rs:18:13 | LL | let _ = v.iter().next(); - | ^^^^^^^^^^^^^^^ help: try calling: `v.get(0)` + | ^^^^^^^^^^^^^^^ help: try calling: `v.first()` error: aborting due to 4 previous errors From e47c5b0e035c556947fcf3485fd2d1823ed1d1b1 Mon Sep 17 00:00:00 2001 From: kyoto7250 <50972773+kyoto7250@users.noreply.github.com> Date: Tue, 24 May 2022 18:56:37 +0900 Subject: [PATCH 26/79] ignore clippy::get_first --- tests/ui-toml/unwrap_used/unwrap_used.rs | 2 +- tests/ui/debug_assert_with_mut_call.rs | 2 +- tests/ui/get_unwrap.fixed | 2 +- tests/ui/get_unwrap.rs | 2 +- tests/ui/needless_lifetimes.rs | 3 +- tests/ui/needless_lifetimes.stderr | 62 ++++++++++++------------ 6 files changed, 37 insertions(+), 36 deletions(-) diff --git a/tests/ui-toml/unwrap_used/unwrap_used.rs b/tests/ui-toml/unwrap_used/unwrap_used.rs index 74d0d7c2650dd..0e82fb20e4558 100644 --- a/tests/ui-toml/unwrap_used/unwrap_used.rs +++ b/tests/ui-toml/unwrap_used/unwrap_used.rs @@ -1,6 +1,6 @@ // compile-flags: --test -#![allow(unused_mut, clippy::from_iter_instead_of_collect)] +#![allow(unused_mut, clippy::get_first, clippy::from_iter_instead_of_collect)] #![warn(clippy::unwrap_used)] #![deny(clippy::get_unwrap)] diff --git a/tests/ui/debug_assert_with_mut_call.rs b/tests/ui/debug_assert_with_mut_call.rs index c5de412556567..46faa0a7b9117 100644 --- a/tests/ui/debug_assert_with_mut_call.rs +++ b/tests/ui/debug_assert_with_mut_call.rs @@ -1,7 +1,7 @@ #![feature(custom_inner_attributes)] #![rustfmt::skip] #![warn(clippy::debug_assert_with_mut_call)] -#![allow(clippy::redundant_closure_call)] +#![allow(clippy::redundant_closure_call, clippy::get_first)] struct S; diff --git a/tests/ui/get_unwrap.fixed b/tests/ui/get_unwrap.fixed index 8f165d675890c..5827fc7d76e60 100644 --- a/tests/ui/get_unwrap.fixed +++ b/tests/ui/get_unwrap.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused_mut, clippy::from_iter_instead_of_collect)] +#![allow(unused_mut, clippy::from_iter_instead_of_collect, clippy::get_first)] #![warn(clippy::unwrap_used)] #![deny(clippy::get_unwrap)] diff --git a/tests/ui/get_unwrap.rs b/tests/ui/get_unwrap.rs index 786749daa746e..a2a323c14fb7c 100644 --- a/tests/ui/get_unwrap.rs +++ b/tests/ui/get_unwrap.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused_mut, clippy::from_iter_instead_of_collect)] +#![allow(unused_mut, clippy::from_iter_instead_of_collect, clippy::get_first)] #![warn(clippy::unwrap_used)] #![deny(clippy::get_unwrap)] diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index 1456204ca8692..fc686b1dac0e1 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -4,7 +4,8 @@ clippy::boxed_local, clippy::needless_pass_by_value, clippy::unnecessary_wraps, - dyn_drop + dyn_drop, + clippy::get_first )] fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index a488bc01fffa2..3c428fd4674ce 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -1,5 +1,5 @@ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:10:1 + --> $DIR/needless_lifetimes.rs:11:1 | LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,181 +7,181 @@ LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} = note: `-D clippy::needless-lifetimes` implied by `-D warnings` error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:12:1 + --> $DIR/needless_lifetimes.rs:13:1 | LL | fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:22:1 + --> $DIR/needless_lifetimes.rs:23:1 | LL | fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:56:1 + --> $DIR/needless_lifetimes.rs:57:1 | LL | fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:61:1 + --> $DIR/needless_lifetimes.rs:62:1 | LL | fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:73:1 + --> $DIR/needless_lifetimes.rs:74:1 | LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:97:1 + --> $DIR/needless_lifetimes.rs:98:1 | LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:127:5 + --> $DIR/needless_lifetimes.rs:128:5 | LL | fn self_and_out<'s>(&'s self) -> &'s u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:136:5 + --> $DIR/needless_lifetimes.rs:137:5 | LL | fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:155:1 + --> $DIR/needless_lifetimes.rs:156:1 | LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:185:1 + --> $DIR/needless_lifetimes.rs:186:1 | LL | fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:191:1 + --> $DIR/needless_lifetimes.rs:192:1 | LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:210:1 + --> $DIR/needless_lifetimes.rs:211:1 | LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:218:1 + --> $DIR/needless_lifetimes.rs:219:1 | LL | fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:254:1 + --> $DIR/needless_lifetimes.rs:255:1 | LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:261:9 + --> $DIR/needless_lifetimes.rs:262:9 | LL | fn needless_lt<'a>(x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:265:9 + --> $DIR/needless_lifetimes.rs:266:9 | LL | fn needless_lt<'a>(_x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:278:9 + --> $DIR/needless_lifetimes.rs:279:9 | LL | fn baz<'a>(&'a self) -> impl Foo + 'a { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:310:5 + --> $DIR/needless_lifetimes.rs:311:5 | LL | fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:319:5 + --> $DIR/needless_lifetimes.rs:320:5 | LL | fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:331:5 + --> $DIR/needless_lifetimes.rs:332:5 | LL | fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:346:5 + --> $DIR/needless_lifetimes.rs:347:5 | LL | fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:359:5 + --> $DIR/needless_lifetimes.rs:360:5 | LL | fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:362:5 + --> $DIR/needless_lifetimes.rs:363:5 | LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:384:9 + --> $DIR/needless_lifetimes.rs:385:9 | LL | fn implicit<'a>(&'a self) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:387:9 + --> $DIR/needless_lifetimes.rs:388:9 | LL | fn implicit_mut<'a>(&'a mut self) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:398:9 + --> $DIR/needless_lifetimes.rs:399:9 | LL | fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:404:9 + --> $DIR/needless_lifetimes.rs:405:9 | LL | fn implicit<'a>(&'a self) -> &'a (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:405:9 + --> $DIR/needless_lifetimes.rs:406:9 | LL | fn implicit_provided<'a>(&'a self) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:414:9 + --> $DIR/needless_lifetimes.rs:415:9 | LL | fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:415:9 + --> $DIR/needless_lifetimes.rs:416:9 | LL | fn lifetime_elsewhere_provided<'a>(self: Box, here: &'a ()) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From d0f93c12a2a6f0d693507ff6325fed62720e616b Mon Sep 17 00:00:00 2001 From: kyoto7250 <50972773+kyoto7250@users.noreply.github.com> Date: Wed, 25 May 2022 09:11:29 +0900 Subject: [PATCH 27/79] refactor: get the required variables with MethodCall --- clippy_lints/src/get_first.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/get_first.rs b/clippy_lints/src/get_first.rs index 0f26c19c49eec..0748ab45252ad 100644 --- a/clippy_lints/src/get_first.rs +++ b/clippy_lints/src/get_first.rs @@ -40,14 +40,11 @@ declare_lint_pass!(GetFirst => [GET_FIRST]); impl<'tcx> LateLintPass<'tcx> for GetFirst { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if_chain! { - if let hir::ExprKind::MethodCall(_, expr_args, _) = &expr.kind; + if let hir::ExprKind::MethodCall(_, [struct_calling_on, method_arg], _) = &expr.kind; if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if match_def_path(cx, expr_def_id, &paths::SLICE_GET) && expr_args.len() == 2; + if match_def_path(cx, expr_def_id, &paths::SLICE_GET); - if let Some(struct_calling_on) = expr_args.get(0); if let Some(_) = is_slice_of_primitives(cx, struct_calling_on); - - if let Some(method_arg) = expr_args.get(1); if let hir::ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = method_arg.kind; then { From d8a281ef735be327dceace003b36e14616023126 Mon Sep 17 00:00:00 2001 From: Micha White Date: Sat, 21 May 2022 17:02:00 -0400 Subject: [PATCH 28/79] Added an unused_rounding lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_nursery.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/unused_rounding.rs | 69 ++++++++++++++++++++++++ tests/ui/unused_rounding.fixed | 8 +++ tests/ui/unused_rounding.rs | 8 +++ tests/ui/unused_rounding.stderr | 22 ++++++++ 8 files changed, 112 insertions(+) create mode 100644 clippy_lints/src/unused_rounding.rs create mode 100644 tests/ui/unused_rounding.fixed create mode 100644 tests/ui/unused_rounding.rs create mode 100644 tests/ui/unused_rounding.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f6295a45f3fb..137fd793e754c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3821,6 +3821,7 @@ Released 2018-09-13 [`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect [`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount [`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label +[`unused_rounding`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_rounding [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit [`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 570d736518bde..02171a0909ed7 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -546,6 +546,7 @@ store.register_lints(&[ unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, unused_async::UNUSED_ASYNC, unused_io_amount::UNUSED_IO_AMOUNT, + unused_rounding::UNUSED_ROUNDING, unused_self::UNUSED_SELF, unused_unit::UNUSED_UNIT, unwrap::PANICKING_UNWRAP, diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index ec187563b3f64..10808af363d70 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -32,5 +32,6 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR), LintId::of(transmute::USELESS_TRANSMUTE), + LintId::of(unused_rounding::UNUSED_ROUNDING), LintId::of(use_self::USE_SELF), ]) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 5f636e5114bdd..09435b06d0a1e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -402,6 +402,7 @@ mod unnested_or_patterns; mod unsafe_removed_from_name; mod unused_async; mod unused_io_amount; +mod unused_rounding; mod unused_self; mod unused_unit; mod unwrap; @@ -906,6 +907,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)); store.register_early_pass(|| Box::new(duplicate_mod::DuplicateMod::default())); store.register_late_pass(|| Box::new(get_first::GetFirst)); + store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/unused_rounding.rs b/clippy_lints/src/unused_rounding.rs new file mode 100644 index 0000000000000..7706a338eb25b --- /dev/null +++ b/clippy_lints/src/unused_rounding.rs @@ -0,0 +1,69 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use rustc_ast::ast::{Expr, ExprKind, LitFloatType, LitKind}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// + /// Detects cases where a whole-number literal float is being rounded, using + /// the `floor`, `ceil`, or `round` methods. + /// + /// ### Why is this bad? + /// + /// This is unnecessary and confusing to the reader. Doing this is probably a mistake. + /// + /// ### Example + /// ```rust + /// let x = 1f32.ceil(); + /// ``` + /// Use instead: + /// ```rust + /// let x = 1f32; + /// ``` + #[clippy::version = "1.62.0"] + pub UNUSED_ROUNDING, + nursery, + "Rounding a whole number literal, which is useless" +} +declare_lint_pass!(UnusedRounding => [UNUSED_ROUNDING]); + +fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> { + if let ExprKind::MethodCall(name_ident, args, _) = &expr.kind + && let method_name = name_ident.ident.name.as_str() + && (method_name == "ceil" || method_name == "round" || method_name == "floor") + && !args.is_empty() + && let ExprKind::Lit(spanned) = &args[0].kind + && let LitKind::Float(symbol, ty) = spanned.kind { + let f = symbol.as_str().parse::().unwrap(); + let f_str = symbol.to_string() + if let LitFloatType::Suffixed(ty) = ty { + ty.name_str() + } else { + "" + }; + if f.fract() < f64::EPSILON { + Some((method_name, f_str)) + } else { + None + } + } else { + None + } +} + +impl EarlyLintPass for UnusedRounding { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if let Some((method_name, float)) = is_useless_rounding(expr) { + span_lint_and_sugg( + cx, + UNUSED_ROUNDING, + expr.span, + &format!("used the `{}` method with a whole number float", method_name), + &format!("remove the `{}` method call", method_name), + float, + Applicability::MachineApplicable, + ); + } + } +} diff --git a/tests/ui/unused_rounding.fixed b/tests/ui/unused_rounding.fixed new file mode 100644 index 0000000000000..a1cb80413d2ca --- /dev/null +++ b/tests/ui/unused_rounding.fixed @@ -0,0 +1,8 @@ +// run-rustfix +#![warn(clippy::unused_rounding)] + +fn main() { + let _ = 1f32; + let _ = 1.0f64; + let _ = 1.00f32; +} diff --git a/tests/ui/unused_rounding.rs b/tests/ui/unused_rounding.rs new file mode 100644 index 0000000000000..90af8f19dd9bd --- /dev/null +++ b/tests/ui/unused_rounding.rs @@ -0,0 +1,8 @@ +// run-rustfix +#![warn(clippy::unused_rounding)] + +fn main() { + let _ = 1f32.ceil(); + let _ = 1.0f64.floor(); + let _ = 1.00f32.round(); +} diff --git a/tests/ui/unused_rounding.stderr b/tests/ui/unused_rounding.stderr new file mode 100644 index 0000000000000..6cfb02e040283 --- /dev/null +++ b/tests/ui/unused_rounding.stderr @@ -0,0 +1,22 @@ +error: used the `ceil` method with a whole number float + --> $DIR/unused_rounding.rs:5:13 + | +LL | let _ = 1f32.ceil(); + | ^^^^^^^^^^^ help: remove the `ceil` method call: `1f32` + | + = note: `-D clippy::unused-rounding` implied by `-D warnings` + +error: used the `floor` method with a whole number float + --> $DIR/unused_rounding.rs:6:13 + | +LL | let _ = 1.0f64.floor(); + | ^^^^^^^^^^^^^^ help: remove the `floor` method call: `1.0f64` + +error: used the `round` method with a whole number float + --> $DIR/unused_rounding.rs:7:13 + | +LL | let _ = 1.00f32.round(); + | ^^^^^^^^^^^^^^^ help: remove the `round` method call: `1.00f32` + +error: aborting due to 3 previous errors + From 4de301e394d99b391de6e449e4dbf44d771d0486 Mon Sep 17 00:00:00 2001 From: Micha White Date: Sun, 22 May 2022 17:21:04 -0400 Subject: [PATCH 29/79] Fixed the test to not use an epsilon --- clippy_lints/src/unused_rounding.rs | 2 +- tests/ui/unused_rounding.fixed | 1 + tests/ui/unused_rounding.rs | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/unused_rounding.rs b/clippy_lints/src/unused_rounding.rs index 7706a338eb25b..3ee5d7a32a36f 100644 --- a/clippy_lints/src/unused_rounding.rs +++ b/clippy_lints/src/unused_rounding.rs @@ -42,7 +42,7 @@ fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> { } else { "" }; - if f.fract() < f64::EPSILON { + if f.fract() == 0.0 { Some((method_name, f_str)) } else { None diff --git a/tests/ui/unused_rounding.fixed b/tests/ui/unused_rounding.fixed index a1cb80413d2ca..54f85806ac3b3 100644 --- a/tests/ui/unused_rounding.fixed +++ b/tests/ui/unused_rounding.fixed @@ -5,4 +5,5 @@ fn main() { let _ = 1f32; let _ = 1.0f64; let _ = 1.00f32; + let _ = 2e-54f64.floor(); } diff --git a/tests/ui/unused_rounding.rs b/tests/ui/unused_rounding.rs index 90af8f19dd9bd..8d007bc4a1dc8 100644 --- a/tests/ui/unused_rounding.rs +++ b/tests/ui/unused_rounding.rs @@ -5,4 +5,5 @@ fn main() { let _ = 1f32.ceil(); let _ = 1.0f64.floor(); let _ = 1.00f32.round(); + let _ = 2e-54f64.floor(); } From f489954e3e6c7b15fd243d33bdd9eabdf90161b6 Mon Sep 17 00:00:00 2001 From: Micha White Date: Sun, 22 May 2022 17:22:10 -0400 Subject: [PATCH 30/79] Changed the lint description --- clippy_lints/src/unused_rounding.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/unused_rounding.rs b/clippy_lints/src/unused_rounding.rs index 3ee5d7a32a36f..306afe4414847 100644 --- a/clippy_lints/src/unused_rounding.rs +++ b/clippy_lints/src/unused_rounding.rs @@ -25,7 +25,7 @@ declare_clippy_lint! { #[clippy::version = "1.62.0"] pub UNUSED_ROUNDING, nursery, - "Rounding a whole number literal, which is useless" + "Uselessly rounding a whole number floating-point literal" } declare_lint_pass!(UnusedRounding => [UNUSED_ROUNDING]); From 2f250783b168a7cb98d608d015e860232362d678 Mon Sep 17 00:00:00 2001 From: Ding Xiang Fei Date: Wed, 25 May 2022 13:52:32 +0800 Subject: [PATCH 31/79] try to cache region_scope_tree as a query --- clippy_lints/src/loops/needless_range_loop.rs | 13 ++----------- clippy_lints/src/shadow.rs | 5 +---- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index e2b82f9fd02ad..4f85364965b69 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -55,11 +55,7 @@ pub(super) fn check<'tcx>( // ensure that the indexed variable was declared before the loop, see #601 if let Some(indexed_extent) = indexed_extent { let parent_def_id = cx.tcx.hir().get_parent_item(expr.hir_id); - let parent_body_id = cx - .tcx - .hir() - .body_owned_by(cx.tcx.hir().local_def_id_to_hir_id(parent_def_id)); - let region_scope_tree = &cx.tcx.typeck_body(parent_body_id).region_scope_tree; + let region_scope_tree = cx.tcx.region_scope_tree(parent_def_id); let pat_extent = region_scope_tree.var_scope(pat.hir_id.local_id).unwrap(); if region_scope_tree.is_subscope_of(indexed_extent, pat_extent) { return; @@ -282,14 +278,9 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { match res { Res::Local(hir_id) => { let parent_def_id = self.cx.tcx.hir().get_parent_item(expr.hir_id); - let parent_body_id = self.cx - .tcx - .hir() - .body_owned_by(self.cx.tcx.hir().local_def_id_to_hir_id(parent_def_id)); let extent = self.cx .tcx - .typeck_body(parent_body_id) - .region_scope_tree + .region_scope_tree(parent_def_id) .var_scope(hir_id.local_id) .unwrap(); if index_used_directly { diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index db32b8d740b3e..2a80e6f918de2 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -162,10 +162,7 @@ fn is_shadow( first: ItemLocalId, second: ItemLocalId, ) -> bool { - let scope_tree = &cx - .tcx - .typeck_body(cx.tcx.hir().body_owned_by(cx.tcx.hir().local_def_id_to_hir_id(owner))) - .region_scope_tree; + let scope_tree = cx.tcx.region_scope_tree(owner); let first_scope = scope_tree.var_scope(first).unwrap(); let second_scope = scope_tree.var_scope(second).unwrap(); scope_tree.is_subscope_of(second_scope, first_scope) From 45be175eeba9b8cc39417cd09216d88a544c4830 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Thu, 19 May 2022 10:49:32 -0400 Subject: [PATCH 32/79] Collect renamed lints --- .../internal_lints/metadata_collector.rs | 55 ++++++++++++++++++- 1 file changed, 52 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 8c1910b3b2af8..7c6263db0ded2 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -7,6 +7,7 @@ //! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such //! a simple mistake) +use crate::renamed_lints::RENAMED_LINTS; use crate::utils::internal_lints::{extract_clippy_version_value, is_lint_ref_type}; use clippy_utils::diagnostics::span_lint; @@ -26,6 +27,7 @@ use rustc_span::{sym, Loc, Span, Symbol}; use serde::{ser::SerializeStruct, Serialize, Serializer}; use std::collections::BinaryHeap; use std::fmt; +use std::fmt::Write as _; use std::fs::{self, OpenOptions}; use std::io::prelude::*; use std::path::Path; @@ -85,6 +87,21 @@ macro_rules! CONFIGURATION_VALUE_TEMPLATE { }; } +macro_rules! RENAMES_SECTION_TEMPLATE { + () => { + r#" +### Past names + +{names} +"# + }; +} +macro_rules! RENAME_VALUE_TEMPLATE { + () => { + "* `{name}`\n" + }; +} + const LINT_EMISSION_FUNCTIONS: [&[&str]; 8] = [ &["clippy_utils", "diagnostics", "span_lint"], &["clippy_utils", "diagnostics", "span_lint_and_help"], @@ -198,9 +215,10 @@ impl Drop for MetadataCollector { // Mapping the final data let mut lints = std::mem::take(&mut self.lints).into_sorted_vec(); - lints - .iter_mut() - .for_each(|x| x.applicability = Some(applicability_info.remove(&x.id).unwrap_or_default())); + collect_renames(&mut lints); + for x in &mut lints { + x.applicability = Some(applicability_info.remove(&x.id).unwrap_or_default()); + } // Outputting if Path::new(OUTPUT_FILE).exists() { @@ -643,6 +661,37 @@ fn is_deprecated_lint(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { false } +fn collect_renames(lints: &mut Vec) { + for lint in lints { + let mut collected = String::new(); + let mut names = vec![lint.id.clone()]; + + loop { + if let Some(lint_name) = names.pop() { + for (k, v) in RENAMED_LINTS { + if_chain! { + if let Some(name) = v.strip_prefix(CLIPPY_LINT_GROUP_PREFIX); + if name == lint_name; + if let Some(past_name) = k.strip_prefix(CLIPPY_LINT_GROUP_PREFIX); + then { + write!(collected, RENAME_VALUE_TEMPLATE!(), name = past_name).unwrap(); + names.push(past_name.to_string()); + } + } + } + + continue; + } + + break; + } + + if !collected.is_empty() { + write!(&mut lint.docs, RENAMES_SECTION_TEMPLATE!(), names = collected).unwrap(); + } + } +} + // ================================================================== // Lint emission // ================================================================== From 1c573c7f1849ecbef1818d862115bd512dcb9cdb Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Wed, 25 May 2022 17:37:57 -0400 Subject: [PATCH 33/79] Remove newlines in [`match_str_case_mismatch`] example --- clippy_lints/src/match_str_case_mismatch.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/clippy_lints/src/match_str_case_mismatch.rs b/clippy_lints/src/match_str_case_mismatch.rs index 85aec93670b95..d97a878825af2 100644 --- a/clippy_lints/src/match_str_case_mismatch.rs +++ b/clippy_lints/src/match_str_case_mismatch.rs @@ -21,7 +21,6 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # let text = "Foo"; - /// /// match &*text.to_ascii_lowercase() { /// "foo" => {}, /// "Bar" => {}, @@ -31,7 +30,6 @@ declare_clippy_lint! { /// Use instead: /// ```rust /// # let text = "Foo"; - /// /// match &*text.to_ascii_lowercase() { /// "foo" => {}, /// "bar" => {}, From 9ee211af9fb04802a23be3557c606d833e288d82 Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Wed, 25 May 2022 19:22:44 -0400 Subject: [PATCH 34/79] Fix `empty_line_after_outer_attribute` false positive --- Cargo.toml | 1 + clippy_lints/src/attrs.rs | 23 +++++++++++++++---- tests/compile-test.rs | 8 +++++-- tests/ui/empty_line_after_outer_attribute.rs | 10 ++++++++ .../empty_line_after_outer_attribute.stderr | 12 +++++----- 5 files changed, 41 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3c8b758d53dca..d23d681df00c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,7 @@ filetime = "0.2" rustc-workspace-hack = "1.0" # UI test dependencies +clap = { version = "3.1", features = ["derive"] } clippy_utils = { path = "clippy_utils" } derive-new = "0.5" if_chain = "1.0" diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 3de91f3d24a91..7105ce4b292ae 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -585,15 +585,21 @@ impl EarlyLintPass for EarlyAttributes { } fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::Item) { - for attr in &item.attrs { + let mut iter = item.attrs.iter().peekable(); + while let Some(attr) = iter.next() { if matches!(attr.kind, AttrKind::Normal(..)) && attr.style == AttrStyle::Outer && is_present_in_source(cx, attr.span) { let begin_of_attr_to_item = Span::new(attr.span.lo(), item.span.lo(), item.span.ctxt(), item.span.parent()); - let end_of_attr_to_item = Span::new(attr.span.hi(), item.span.lo(), item.span.ctxt(), item.span.parent()); + let end_of_attr_to_next_attr_or_item = Span::new( + attr.span.hi(), + iter.peek().map_or(item.span.lo(), |next_attr| next_attr.span.lo()), + item.span.ctxt(), + item.span.parent(), + ); - if let Some(snippet) = snippet_opt(cx, end_of_attr_to_item) { + if let Some(snippet) = snippet_opt(cx, end_of_attr_to_next_attr_or_item) { let lines = snippet.split('\n').collect::>(); let lines = without_block_comments(lines); @@ -623,8 +629,15 @@ fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: Opti if feature_item.has_name(sym::rustfmt); // check for `rustfmt_skip` and `rustfmt::skip` if let Some(skip_item) = &items[1].meta_item(); - if skip_item.has_name(sym!(rustfmt_skip)) || - skip_item.path.segments.last().expect("empty path in attribute").ident.name == sym::skip; + if skip_item.has_name(sym!(rustfmt_skip)) + || skip_item + .path + .segments + .last() + .expect("empty path in attribute") + .ident + .name + == sym::skip; // Only lint outer attributes, because custom inner attributes are unstable // Tracking issue: https://github.com/rust-lang/rust/issues/54726 if attr.style == AttrStyle::Outer; diff --git a/tests/compile-test.rs b/tests/compile-test.rs index c9710e3db8e8d..18099c45fee3c 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -22,6 +22,7 @@ const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal"); /// All crates used in UI tests are listed here static TEST_DEPENDENCIES: &[&str] = &[ + "clap", "clippy_utils", "derive_new", "futures", @@ -40,6 +41,8 @@ static TEST_DEPENDENCIES: &[&str] = &[ // Test dependencies may need an `extern crate` here to ensure that they show up // in the depinfo file (otherwise cargo thinks they are unused) #[allow(unused_extern_crates)] +extern crate clap; +#[allow(unused_extern_crates)] extern crate clippy_utils; #[allow(unused_extern_crates)] extern crate derive_new; @@ -109,8 +112,9 @@ static EXTERN_FLAGS: SyncLazy = SyncLazy::new(|| { not_found.is_empty(), "dependencies not found in depinfo: {:?}\n\ help: Make sure the `-Z binary-dep-depinfo` rust flag is enabled\n\ - help: Try adding to dev-dependencies in Cargo.toml", - not_found + help: Try adding to dev-dependencies in Cargo.toml\n\ + help: Be sure to also add `extern crate ...;` to tests/compile-test.rs", + not_found, ); crates .into_iter() diff --git a/tests/ui/empty_line_after_outer_attribute.rs b/tests/ui/empty_line_after_outer_attribute.rs index 3e92bca986ab5..d15c84d7438a0 100644 --- a/tests/ui/empty_line_after_outer_attribute.rs +++ b/tests/ui/empty_line_after_outer_attribute.rs @@ -4,6 +4,9 @@ #![feature(custom_inner_attributes)] #![rustfmt::skip] +#[macro_use] +extern crate clap; + #[macro_use] extern crate proc_macro_attr; @@ -110,4 +113,11 @@ pub trait Bazz { } } +#[derive(clap::Parser)] +#[clap(after_help = "This ia a help message. + +You're welcome. +")] +pub struct Args; + fn main() {} diff --git a/tests/ui/empty_line_after_outer_attribute.stderr b/tests/ui/empty_line_after_outer_attribute.stderr index 594fca44a3210..acc3edef9b928 100644 --- a/tests/ui/empty_line_after_outer_attribute.stderr +++ b/tests/ui/empty_line_after_outer_attribute.stderr @@ -1,5 +1,5 @@ error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:11:1 + --> $DIR/empty_line_after_outer_attribute.rs:14:1 | LL | / #[crate_type = "lib"] LL | | @@ -10,7 +10,7 @@ LL | | fn with_one_newline_and_comment() { assert!(true) } = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings` error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:23:1 + --> $DIR/empty_line_after_outer_attribute.rs:26:1 | LL | / #[crate_type = "lib"] LL | | @@ -18,7 +18,7 @@ LL | | fn with_one_newline() { assert!(true) } | |_ error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:28:1 + --> $DIR/empty_line_after_outer_attribute.rs:31:1 | LL | / #[crate_type = "lib"] LL | | @@ -27,7 +27,7 @@ LL | | fn with_two_newlines() { assert!(true) } | |_ error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:35:1 + --> $DIR/empty_line_after_outer_attribute.rs:38:1 | LL | / #[crate_type = "lib"] LL | | @@ -35,7 +35,7 @@ LL | | enum Baz { | |_ error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:43:1 + --> $DIR/empty_line_after_outer_attribute.rs:46:1 | LL | / #[crate_type = "lib"] LL | | @@ -43,7 +43,7 @@ LL | | struct Foo { | |_ error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:51:1 + --> $DIR/empty_line_after_outer_attribute.rs:54:1 | LL | / #[crate_type = "lib"] LL | | From 17f70478116d3b77783db2ed6453697a77a7d769 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Thu, 26 May 2022 14:21:09 +0000 Subject: [PATCH 35/79] Only return DefIds to Fn-like definitions in clippy_utils::fn_def_id --- clippy_utils/src/lib.rs | 12 ++++++++- tests/ui/crashes/ice-8850.rs | 27 +++++++++++++++++++ tests/ui/crashes/ice-8850.stderr | 45 ++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 tests/ui/crashes/ice-8850.rs create mode 100644 tests/ui/crashes/ice-8850.stderr diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index adb37cc9d7510..b8a80d4adc31b 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1916,7 +1916,17 @@ pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { .. }, .., - ) => cx.typeck_results().qpath_res(qpath, *path_hir_id).opt_def_id(), + ) => { + // Only return Fn-like DefIds, not the DefIds of statics/consts/etc that contain or + // deref to fn pointers, dyn Fn, impl Fn - #8850 + if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) = + cx.typeck_results().qpath_res(qpath, *path_hir_id) + { + Some(id) + } else { + None + } + }, _ => None, } } diff --git a/tests/ui/crashes/ice-8850.rs b/tests/ui/crashes/ice-8850.rs new file mode 100644 index 0000000000000..f2747ab2239a6 --- /dev/null +++ b/tests/ui/crashes/ice-8850.rs @@ -0,0 +1,27 @@ +fn fn_pointer_static() -> usize { + static FN: fn() -> usize = || 1; + let res = FN() + 1; + res +} + +fn fn_pointer_const() -> usize { + const FN: fn() -> usize = || 1; + let res = FN() + 1; + res +} + +fn deref_to_dyn_fn() -> usize { + struct Derefs; + impl std::ops::Deref for Derefs { + type Target = dyn Fn() -> usize; + + fn deref(&self) -> &Self::Target { + &|| 2 + } + } + static FN: Derefs = Derefs; + let res = FN() + 1; + res +} + +fn main() {} diff --git a/tests/ui/crashes/ice-8850.stderr b/tests/ui/crashes/ice-8850.stderr new file mode 100644 index 0000000000000..620fd1edaf790 --- /dev/null +++ b/tests/ui/crashes/ice-8850.stderr @@ -0,0 +1,45 @@ +error: returning the result of a `let` binding from a block + --> $DIR/ice-8850.rs:4:5 + | +LL | let res = FN() + 1; + | ------------------- unnecessary `let` binding +LL | res + | ^^^ + | + = note: `-D clippy::let-and-return` implied by `-D warnings` +help: return the expression directly + | +LL ~ +LL ~ FN() + 1 + | + +error: returning the result of a `let` binding from a block + --> $DIR/ice-8850.rs:10:5 + | +LL | let res = FN() + 1; + | ------------------- unnecessary `let` binding +LL | res + | ^^^ + | +help: return the expression directly + | +LL ~ +LL ~ FN() + 1 + | + +error: returning the result of a `let` binding from a block + --> $DIR/ice-8850.rs:24:5 + | +LL | let res = FN() + 1; + | ------------------- unnecessary `let` binding +LL | res + | ^^^ + | +help: return the expression directly + | +LL ~ +LL ~ FN() + 1 + | + +error: aborting due to 3 previous errors + From ea06a414f1379e4470a5766b6587e610341081b7 Mon Sep 17 00:00:00 2001 From: tamaron Date: Fri, 27 May 2022 01:30:44 +0900 Subject: [PATCH 36/79] fix --- clippy_lints/src/dbg_macro.rs | 21 +++- clippy_lints/src/lib.rs | 3 +- clippy_lints/src/utils/conf.rs | 4 + tests/ui-toml/dbg_macro/clippy.toml | 1 + tests/ui-toml/dbg_macro/dbg_macro.rs | 39 +++++++ tests/ui-toml/dbg_macro/dbg_macro.stderr | 102 ++++++++++++++++++ .../toml_unknown_key/conf_unknown_key.stderr | 1 + tests/ui/dbg_macro.stderr | 35 +++++- 8 files changed, 200 insertions(+), 6 deletions(-) create mode 100644 tests/ui-toml/dbg_macro/clippy.toml create mode 100644 tests/ui-toml/dbg_macro/dbg_macro.rs create mode 100644 tests/ui-toml/dbg_macro/dbg_macro.stderr diff --git a/clippy_lints/src/dbg_macro.rs b/clippy_lints/src/dbg_macro.rs index f99d793c201d7..17deccf8c3930 100644 --- a/clippy_lints/src/dbg_macro.rs +++ b/clippy_lints/src/dbg_macro.rs @@ -5,7 +5,7 @@ use clippy_utils::{is_in_cfg_test, is_in_test_function}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::sym; declare_clippy_lint! { @@ -30,14 +30,27 @@ declare_clippy_lint! { "`dbg!` macro is intended as a debugging tool" } -declare_lint_pass!(DbgMacro => [DBG_MACRO]); +#[derive(Copy, Clone)] +pub struct DbgMacro { + allow_dbg_in_tests: bool, +} + +impl_lint_pass!(DbgMacro => [DBG_MACRO]); + +impl DbgMacro { + pub fn new(allow_dbg_in_tests: bool) -> Self { + DbgMacro { allow_dbg_in_tests } + } +} impl LateLintPass<'_> for DbgMacro { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; if cx.tcx.is_diagnostic_item(sym::dbg_macro, macro_call.def_id) { - // we make an exception for test code - if is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id) { + // allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml + if self.allow_dbg_in_tests + && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)) + { return; } let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 09435b06d0a1e..ffbf9a6591708 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -888,7 +888,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation)); store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion)); store.register_late_pass(|| Box::new(significant_drop_in_scrutinee::SignificantDropInScrutinee)); - store.register_late_pass(|| Box::new(dbg_macro::DbgMacro)); + let allow_dbg_in_tests = conf.allow_dbg_in_tests; + store.register_late_pass(move || Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests))); let cargo_ignore_publish = conf.cargo_ignore_publish; store.register_late_pass(move || { Box::new(cargo::Cargo { diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index cd4d16fe95f75..b5c5d35135f90 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -340,6 +340,10 @@ define_Conf! { /// /// Whether `unwrap` should be allowed in test functions (allow_unwrap_in_tests: bool = false), + /// Lint: DBG_MACRO. + /// + /// Whether `dbg!` should be allowed in test functions + (allow_dbg_in_tests: bool = false), } /// Search for the configuration file. diff --git a/tests/ui-toml/dbg_macro/clippy.toml b/tests/ui-toml/dbg_macro/clippy.toml new file mode 100644 index 0000000000000..4296655a040fa --- /dev/null +++ b/tests/ui-toml/dbg_macro/clippy.toml @@ -0,0 +1 @@ +allow-dbg-in-tests = true diff --git a/tests/ui-toml/dbg_macro/dbg_macro.rs b/tests/ui-toml/dbg_macro/dbg_macro.rs new file mode 100644 index 0000000000000..5d9ce18f631b6 --- /dev/null +++ b/tests/ui-toml/dbg_macro/dbg_macro.rs @@ -0,0 +1,39 @@ +// compile-flags: --test +#![warn(clippy::dbg_macro)] + +fn foo(n: u32) -> u32 { + if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } +} + +fn factorial(n: u32) -> u32 { + if dbg!(n <= 1) { + dbg!(1) + } else { + dbg!(n * factorial(n - 1)) + } +} + +fn main() { + dbg!(42); + dbg!(dbg!(dbg!(42))); + foo(3) + dbg!(factorial(4)); + dbg!(1, 2, dbg!(3, 4)); + dbg!(1, 2, 3, 4, 5); +} + +#[test] +pub fn issue8481() { + dbg!(1); +} + +#[cfg(test)] +fn foo2() { + dbg!(1); +} + +#[cfg(test)] +mod mod1 { + fn func() { + dbg!(1); + } +} diff --git a/tests/ui-toml/dbg_macro/dbg_macro.stderr b/tests/ui-toml/dbg_macro/dbg_macro.stderr new file mode 100644 index 0000000000000..46efb86dcfc5f --- /dev/null +++ b/tests/ui-toml/dbg_macro/dbg_macro.stderr @@ -0,0 +1,102 @@ +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:5:22 + | +LL | if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::dbg-macro` implied by `-D warnings` +help: ensure to avoid having uses of it in version control + | +LL | if let Some(n) = n.checked_sub(4) { n } else { n } + | ~~~~~~~~~~~~~~~~ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:9:8 + | +LL | if dbg!(n <= 1) { + | ^^^^^^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | if n <= 1 { + | ~~~~~~ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:10:9 + | +LL | dbg!(1) + | ^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | 1 + | + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:12:9 + | +LL | dbg!(n * factorial(n - 1)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | n * factorial(n - 1) + | + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:17:5 + | +LL | dbg!(42); + | ^^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | 42; + | ~~ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:18:5 + | +LL | dbg!(dbg!(dbg!(42))); + | ^^^^^^^^^^^^^^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | dbg!(dbg!(42)); + | ~~~~~~~~~~~~~~ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:19:14 + | +LL | foo(3) + dbg!(factorial(4)); + | ^^^^^^^^^^^^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | foo(3) + factorial(4); + | ~~~~~~~~~~~~ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:20:5 + | +LL | dbg!(1, 2, dbg!(3, 4)); + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | (1, 2, dbg!(3, 4)); + | ~~~~~~~~~~~~~~~~~~ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:21:5 + | +LL | dbg!(1, 2, 3, 4, 5); + | ^^^^^^^^^^^^^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | (1, 2, 3, 4, 5); + | ~~~~~~~~~~~~~~~ + +error: aborting due to 9 previous errors + diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 92838aa57dfda..1d87fd91a2532 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,4 +1,5 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of + allow-dbg-in-tests allow-expect-in-tests allow-unwrap-in-tests allowed-scripts diff --git a/tests/ui/dbg_macro.stderr b/tests/ui/dbg_macro.stderr index a3e7a7162e51d..e6a65b46d975c 100644 --- a/tests/ui/dbg_macro.stderr +++ b/tests/ui/dbg_macro.stderr @@ -109,5 +109,38 @@ help: ensure to avoid having uses of it in version control LL | 2; | ~ -error: aborting due to 10 previous errors +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:47:5 + | +LL | dbg!(1); + | ^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | 1; + | ~ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:52:5 + | +LL | dbg!(1); + | ^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | 1; + | ~ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:58:9 + | +LL | dbg!(1); + | ^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | 1; + | ~ + +error: aborting due to 13 previous errors From fc28f6acc8ad5e1b6bd8c8963eef28ac02df59d2 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Tue, 24 May 2022 21:56:44 -0400 Subject: [PATCH 37/79] Support `Weak` in [`rc_clone_in_vec_init`] --- clippy_lints/src/rc_clone_in_vec_init.rs | 56 +++--- tests/ui/rc_clone_in_vec_init/arc.stderr | 16 +- tests/ui/rc_clone_in_vec_init/rc.stderr | 16 +- tests/ui/rc_clone_in_vec_init/weak.rs | 83 +++++++++ tests/ui/rc_clone_in_vec_init/weak.stderr | 201 ++++++++++++++++++++++ 5 files changed, 327 insertions(+), 45 deletions(-) create mode 100644 tests/ui/rc_clone_in_vec_init/weak.rs create mode 100644 tests/ui/rc_clone_in_vec_init/weak.stderr diff --git a/clippy_lints/src/rc_clone_in_vec_init.rs b/clippy_lints/src/rc_clone_in_vec_init.rs index 110f58f3734df..8db8c4e9b7870 100644 --- a/clippy_lints/src/rc_clone_in_vec_init.rs +++ b/clippy_lints/src/rc_clone_in_vec_init.rs @@ -2,7 +2,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::VecArgs; use clippy_utils::last_path_segment; use clippy_utils::macros::root_macro_call_first_node; +use clippy_utils::paths; use clippy_utils::source::{indent_of, snippet}; +use clippy_utils::ty::match_type; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -11,10 +13,11 @@ use rustc_span::{sym, Span, Symbol}; declare_clippy_lint! { /// ### What it does - /// Checks for `Arc::new` or `Rc::new` in `vec![elem; len]` + /// Checks for reference-counted pointers (`Arc`, `Rc`, `rc::Weak`, and `sync::Weak`) + /// in `vec![elem; len]` /// /// ### Why is this bad? - /// This will create `elem` once and clone it `len` times - doing so with `Arc` or `Rc` + /// This will create `elem` once and clone it `len` times - doing so with `Arc`/`Rc`/`Weak` /// is a bit misleading, as it will create references to the same pointer, rather /// than different instances. /// @@ -26,7 +29,6 @@ declare_clippy_lint! { /// ``` /// Use instead: /// ```rust - /// /// // Initialize each value separately: /// let mut data = Vec::with_capacity(100); /// for _ in 0..100 { @@ -42,7 +44,7 @@ declare_clippy_lint! { #[clippy::version = "1.62.0"] pub RC_CLONE_IN_VEC_INIT, suspicious, - "initializing `Arc` or `Rc` in `vec![elem; len]`" + "initializing reference-counted pointer in `vec![elem; len]`" } declare_lint_pass!(RcCloneInVecInit => [RC_CLONE_IN_VEC_INIT]); @@ -50,26 +52,12 @@ impl LateLintPass<'_> for RcCloneInVecInit { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return; }; let Some(VecArgs::Repeat(elem, len)) = VecArgs::hir(cx, expr) else { return; }; - let Some(symbol) = new_reference_call(cx, elem) else { return; }; + let Some((symbol, func_span)) = ref_init(cx, elem) else { return; }; - emit_lint(cx, symbol, macro_call.span, elem, len); + emit_lint(cx, symbol, macro_call.span, elem, len, func_span); } } -fn elem_snippet(cx: &LateContext<'_>, elem: &Expr<'_>, symbol_name: &str) -> String { - let elem_snippet = snippet(cx, elem.span, "..").to_string(); - if elem_snippet.contains('\n') { - // This string must be found in `elem_snippet`, otherwise we won't be constructing - // the snippet in the first place. - let reference_creation = format!("{symbol_name}::new"); - let (code_until_reference_creation, _right) = elem_snippet.split_once(&reference_creation).unwrap(); - - return format!("{code_until_reference_creation}{reference_creation}(..)"); - } - - elem_snippet -} - fn loop_init_suggestion(elem: &str, len: &str, indent: &str) -> String { format!( r#"{{ @@ -89,17 +77,17 @@ fn extract_suggestion(elem: &str, len: &str, indent: &str) -> String { ) } -fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, elem: &Expr<'_>, len: &Expr<'_>) { +fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, elem: &Expr<'_>, len: &Expr<'_>, func_span: Span) { let symbol_name = symbol.as_str(); span_lint_and_then( cx, RC_CLONE_IN_VEC_INIT, lint_span, - &format!("calling `{symbol_name}::new` in `vec![elem; len]`"), + "initializing a reference-counted pointer in `vec![elem; len]`", |diag| { let len_snippet = snippet(cx, len.span, ".."); - let elem_snippet = elem_snippet(cx, elem, symbol_name); + let elem_snippet = format!("{}(..)", snippet(cx, elem.span.with_hi(func_span.hi()), "..")); let indentation = " ".repeat(indent_of(cx, lint_span).unwrap_or(0)); let loop_init_suggestion = loop_init_suggestion(&elem_snippet, len_snippet.as_ref(), &indentation); let extract_suggestion = extract_suggestion(&elem_snippet, len_snippet.as_ref(), &indentation); @@ -109,7 +97,7 @@ fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, elem: &Expr< lint_span, format!("consider initializing each `{symbol_name}` element individually"), loop_init_suggestion, - Applicability::Unspecified, + Applicability::HasPlaceholders, ); diag.span_suggestion( lint_span, @@ -117,23 +105,33 @@ fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, elem: &Expr< "or if this is intentional, consider extracting the `{symbol_name}` initialization to a variable" ), extract_suggestion, - Applicability::Unspecified, + Applicability::HasPlaceholders, ); }, ); } -/// Checks whether the given `expr` is a call to `Arc::new` or `Rc::new` -fn new_reference_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { +/// Checks whether the given `expr` is a call to `Arc::new`, `Rc::new`, or evaluates to a `Weak` +fn ref_init(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<(Symbol, Span)> { if_chain! { if let ExprKind::Call(func, _args) = expr.kind; if let ExprKind::Path(ref func_path @ QPath::TypeRelative(ty, _)) = func.kind; if let TyKind::Path(ref ty_path) = ty.kind; if let Some(def_id) = cx.qpath_res(ty_path, ty.hir_id).opt_def_id(); - if last_path_segment(func_path).ident.name == sym::new; then { - return cx.tcx.get_diagnostic_name(def_id).filter(|symbol| symbol == &sym::Arc || symbol == &sym::Rc); + if last_path_segment(func_path).ident.name == sym::new + && let Some(symbol) = cx + .tcx + .get_diagnostic_name(def_id) + .filter(|symbol| symbol == &sym::Arc || symbol == &sym::Rc) { + return Some((symbol, func.span)); + } + + let ty_path = cx.typeck_results().expr_ty(expr); + if match_type(cx, ty_path, &paths::WEAK_RC) || match_type(cx, ty_path, &paths::WEAK_ARC) { + return Some((Symbol::intern("Weak"), func.span)); + } } } diff --git a/tests/ui/rc_clone_in_vec_init/arc.stderr b/tests/ui/rc_clone_in_vec_init/arc.stderr index ce84186c8e300..cd7d91e120650 100644 --- a/tests/ui/rc_clone_in_vec_init/arc.stderr +++ b/tests/ui/rc_clone_in_vec_init/arc.stderr @@ -1,4 +1,4 @@ -error: calling `Arc::new` in `vec![elem; len]` +error: initializing a reference-counted pointer in `vec![elem; len]` --> $DIR/arc.rs:7:13 | LL | let v = vec![Arc::new("x".to_string()); 2]; @@ -10,19 +10,19 @@ help: consider initializing each `Arc` element individually | LL ~ let v = { LL + let mut v = Vec::with_capacity(2); -LL + (0..2).for_each(|_| v.push(Arc::new("x".to_string()))); +LL + (0..2).for_each(|_| v.push(Arc::new(..))); LL + v LL ~ }; | help: or if this is intentional, consider extracting the `Arc` initialization to a variable | LL ~ let v = { -LL + let data = Arc::new("x".to_string()); +LL + let data = Arc::new(..); LL + vec![data; 2] LL ~ }; | -error: calling `Arc::new` in `vec![elem; len]` +error: initializing a reference-counted pointer in `vec![elem; len]` --> $DIR/arc.rs:15:21 | LL | let v = vec![Arc::new("x".to_string()); 2]; @@ -33,19 +33,19 @@ help: consider initializing each `Arc` element individually | LL ~ let v = { LL + let mut v = Vec::with_capacity(2); -LL + (0..2).for_each(|_| v.push(Arc::new("x".to_string()))); +LL + (0..2).for_each(|_| v.push(Arc::new(..))); LL + v LL ~ }; | help: or if this is intentional, consider extracting the `Arc` initialization to a variable | LL ~ let v = { -LL + let data = Arc::new("x".to_string()); +LL + let data = Arc::new(..); LL + vec![data; 2] LL ~ }; | -error: calling `Arc::new` in `vec![elem; len]` +error: initializing a reference-counted pointer in `vec![elem; len]` --> $DIR/arc.rs:21:13 | LL | let v = vec![ @@ -75,7 +75,7 @@ LL + vec![data; 2] LL ~ }; | -error: calling `Arc::new` in `vec![elem; len]` +error: initializing a reference-counted pointer in `vec![elem; len]` --> $DIR/arc.rs:30:14 | LL | let v1 = vec![ diff --git a/tests/ui/rc_clone_in_vec_init/rc.stderr b/tests/ui/rc_clone_in_vec_init/rc.stderr index 0f5cc0cf98fea..fe861afe05491 100644 --- a/tests/ui/rc_clone_in_vec_init/rc.stderr +++ b/tests/ui/rc_clone_in_vec_init/rc.stderr @@ -1,4 +1,4 @@ -error: calling `Rc::new` in `vec![elem; len]` +error: initializing a reference-counted pointer in `vec![elem; len]` --> $DIR/rc.rs:8:13 | LL | let v = vec![Rc::new("x".to_string()); 2]; @@ -10,19 +10,19 @@ help: consider initializing each `Rc` element individually | LL ~ let v = { LL + let mut v = Vec::with_capacity(2); -LL + (0..2).for_each(|_| v.push(Rc::new("x".to_string()))); +LL + (0..2).for_each(|_| v.push(Rc::new(..))); LL + v LL ~ }; | help: or if this is intentional, consider extracting the `Rc` initialization to a variable | LL ~ let v = { -LL + let data = Rc::new("x".to_string()); +LL + let data = Rc::new(..); LL + vec![data; 2] LL ~ }; | -error: calling `Rc::new` in `vec![elem; len]` +error: initializing a reference-counted pointer in `vec![elem; len]` --> $DIR/rc.rs:16:21 | LL | let v = vec![Rc::new("x".to_string()); 2]; @@ -33,19 +33,19 @@ help: consider initializing each `Rc` element individually | LL ~ let v = { LL + let mut v = Vec::with_capacity(2); -LL + (0..2).for_each(|_| v.push(Rc::new("x".to_string()))); +LL + (0..2).for_each(|_| v.push(Rc::new(..))); LL + v LL ~ }; | help: or if this is intentional, consider extracting the `Rc` initialization to a variable | LL ~ let v = { -LL + let data = Rc::new("x".to_string()); +LL + let data = Rc::new(..); LL + vec![data; 2] LL ~ }; | -error: calling `Rc::new` in `vec![elem; len]` +error: initializing a reference-counted pointer in `vec![elem; len]` --> $DIR/rc.rs:22:13 | LL | let v = vec![ @@ -75,7 +75,7 @@ LL + vec![data; 2] LL ~ }; | -error: calling `Rc::new` in `vec![elem; len]` +error: initializing a reference-counted pointer in `vec![elem; len]` --> $DIR/rc.rs:31:14 | LL | let v1 = vec![ diff --git a/tests/ui/rc_clone_in_vec_init/weak.rs b/tests/ui/rc_clone_in_vec_init/weak.rs new file mode 100644 index 0000000000000..693c9b553c562 --- /dev/null +++ b/tests/ui/rc_clone_in_vec_init/weak.rs @@ -0,0 +1,83 @@ +#![warn(clippy::rc_clone_in_vec_init)] +use std::rc::{Rc, Weak as UnSyncWeak}; +use std::sync::{Arc, Mutex, Weak as SyncWeak}; + +fn main() {} + +fn should_warn_simple_case() { + let v = vec![SyncWeak::::new(); 2]; + let v2 = vec![UnSyncWeak::::new(); 2]; + + let v = vec![Rc::downgrade(&Rc::new("x".to_string())); 2]; + let v = vec![Arc::downgrade(&Arc::new("x".to_string())); 2]; +} + +fn should_warn_simple_case_with_big_indentation() { + if true { + let k = 1; + dbg!(k); + if true { + let v = vec![Arc::downgrade(&Arc::new("x".to_string())); 2]; + let v2 = vec![Rc::downgrade(&Rc::new("x".to_string())); 2]; + } + } +} + +fn should_warn_complex_case() { + let v = vec![ + Arc::downgrade(&Arc::new(Mutex::new({ + let x = 1; + dbg!(x); + x + }))); + 2 + ]; + + let v1 = vec![ + Rc::downgrade(&Rc::new(Mutex::new({ + let x = 1; + dbg!(x); + x + }))); + 2 + ]; +} + +fn should_not_warn_custom_weak() { + #[derive(Clone)] + struct Weak; + + impl Weak { + fn new() -> Self { + Weak + } + } + + let v = vec![Weak::new(); 2]; +} + +fn should_not_warn_vec_from_elem_but_not_weak() { + let v = vec![String::new(); 2]; + let v1 = vec![1; 2]; + let v2 = vec![ + Box::new(Arc::downgrade(&Arc::new({ + let y = 3; + dbg!(y); + y + }))); + 2 + ]; + let v3 = vec![ + Box::new(Rc::downgrade(&Rc::new({ + let y = 3; + dbg!(y); + y + }))); + 2 + ]; +} + +fn should_not_warn_vec_macro_but_not_from_elem() { + let v = vec![Arc::downgrade(&Arc::new("x".to_string()))]; + let v = vec![Rc::downgrade(&Rc::new("x".to_string()))]; +} diff --git a/tests/ui/rc_clone_in_vec_init/weak.stderr b/tests/ui/rc_clone_in_vec_init/weak.stderr new file mode 100644 index 0000000000000..4a21946ccdfa8 --- /dev/null +++ b/tests/ui/rc_clone_in_vec_init/weak.stderr @@ -0,0 +1,201 @@ +error: initializing a reference-counted pointer in `vec![elem; len]` + --> $DIR/weak.rs:8:13 + | +LL | let v = vec![SyncWeak::::new(); 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::rc-clone-in-vec-init` implied by `-D warnings` + = note: each element will point to the same `Weak` instance +help: consider initializing each `Weak` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(SyncWeak::::new(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Weak` initialization to a variable + | +LL ~ let v = { +LL + let data = SyncWeak::::new(..); +LL + vec![data; 2] +LL ~ }; + | + +error: initializing a reference-counted pointer in `vec![elem; len]` + --> $DIR/weak.rs:9:14 + | +LL | let v2 = vec![UnSyncWeak::::new(); 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: each element will point to the same `Weak` instance +help: consider initializing each `Weak` element individually + | +LL ~ let v2 = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(UnSyncWeak::::new(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Weak` initialization to a variable + | +LL ~ let v2 = { +LL + let data = UnSyncWeak::::new(..); +LL + vec![data; 2] +LL ~ }; + | + +error: initializing a reference-counted pointer in `vec![elem; len]` + --> $DIR/weak.rs:11:13 + | +LL | let v = vec![Rc::downgrade(&Rc::new("x".to_string())); 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: each element will point to the same `Weak` instance +help: consider initializing each `Weak` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Rc::downgrade(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Weak` initialization to a variable + | +LL ~ let v = { +LL + let data = Rc::downgrade(..); +LL + vec![data; 2] +LL ~ }; + | + +error: initializing a reference-counted pointer in `vec![elem; len]` + --> $DIR/weak.rs:12:13 + | +LL | let v = vec![Arc::downgrade(&Arc::new("x".to_string())); 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: each element will point to the same `Weak` instance +help: consider initializing each `Weak` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Arc::downgrade(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Weak` initialization to a variable + | +LL ~ let v = { +LL + let data = Arc::downgrade(..); +LL + vec![data; 2] +LL ~ }; + | + +error: initializing a reference-counted pointer in `vec![elem; len]` + --> $DIR/weak.rs:20:21 + | +LL | let v = vec![Arc::downgrade(&Arc::new("x".to_string())); 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: each element will point to the same `Weak` instance +help: consider initializing each `Weak` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Arc::downgrade(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Weak` initialization to a variable + | +LL ~ let v = { +LL + let data = Arc::downgrade(..); +LL + vec![data; 2] +LL ~ }; + | + +error: initializing a reference-counted pointer in `vec![elem; len]` + --> $DIR/weak.rs:21:22 + | +LL | let v2 = vec![Rc::downgrade(&Rc::new("x".to_string())); 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: each element will point to the same `Weak` instance +help: consider initializing each `Weak` element individually + | +LL ~ let v2 = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Rc::downgrade(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Weak` initialization to a variable + | +LL ~ let v2 = { +LL + let data = Rc::downgrade(..); +LL + vec![data; 2] +LL ~ }; + | + +error: initializing a reference-counted pointer in `vec![elem; len]` + --> $DIR/weak.rs:27:13 + | +LL | let v = vec![ + | _____________^ +LL | | Arc::downgrade(&Arc::new(Mutex::new({ +LL | | let x = 1; +LL | | dbg!(x); +... | +LL | | 2 +LL | | ]; + | |_____^ + | + = note: each element will point to the same `Weak` instance +help: consider initializing each `Weak` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Arc::downgrade(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Weak` initialization to a variable + | +LL ~ let v = { +LL + let data = Arc::downgrade(..); +LL + vec![data; 2] +LL ~ }; + | + +error: initializing a reference-counted pointer in `vec![elem; len]` + --> $DIR/weak.rs:36:14 + | +LL | let v1 = vec![ + | ______________^ +LL | | Rc::downgrade(&Rc::new(Mutex::new({ +LL | | let x = 1; +LL | | dbg!(x); +... | +LL | | 2 +LL | | ]; + | |_____^ + | + = note: each element will point to the same `Weak` instance +help: consider initializing each `Weak` element individually + | +LL ~ let v1 = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Rc::downgrade(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Weak` initialization to a variable + | +LL ~ let v1 = { +LL + let data = Rc::downgrade(..); +LL + vec![data; 2] +LL ~ }; + | + +error: aborting due to 8 previous errors + From e33d87d4a0a188aa03d7d07700dbf296cf76581a Mon Sep 17 00:00:00 2001 From: Preston From Date: Tue, 24 May 2022 01:06:33 -0600 Subject: [PATCH 38/79] When setting suggestion, add suggestion for MoveAndClone for non-ref When trying to set the current suggestion, if the type of the expression is not a reference and it is not trivially pure clone copy, we should still trigger and emit a lint message. Since this fix may require cloning an expensive-to-clone type, do not attempt to offer a suggested fix. This change means that matches generated from TryDesugar and AwaitDesugar would normally trigger a lint, but they are out of scope for this lint, so we will explicitly ignore matches with sources of TryDesugar or AwaitDesugar. changelog: Update for [`significant_drop_in_scrutinee`] to correctly emit lint messages for cases where the type is not a reference and not trivially pure clone copy. --- .../src/significant_drop_in_scrutinee.rs | 107 ++++++++++-------- clippy_utils/src/lib.rs | 3 +- tests/ui/significant_drop_in_scrutinee.rs | 39 +++++++ tests/ui/significant_drop_in_scrutinee.stderr | 60 ++++++---- 4 files changed, 134 insertions(+), 75 deletions(-) diff --git a/clippy_lints/src/significant_drop_in_scrutinee.rs b/clippy_lints/src/significant_drop_in_scrutinee.rs index 424b361a905ce..6b45b6d4452fb 100644 --- a/clippy_lints/src/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/significant_drop_in_scrutinee.rs @@ -4,7 +4,7 @@ use clippy_utils::get_attr; use clippy_utils::source::{indent_of, snippet}; use rustc_errors::{Applicability, Diagnostic}; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{Expr, ExprKind, MatchSource}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{Ty, TypeAndMut}; @@ -154,9 +154,17 @@ fn has_significant_drop_in_scrutinee<'tcx, 'a>( cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>, ) -> Option> { - let mut helper = SigDropHelper::new(cx); match expr.kind { - ExprKind::Match(match_expr, _, _) => helper.find_sig_drop(match_expr), + ExprKind::Match(match_expr, _, source) => { + match source { + MatchSource::Normal | MatchSource::ForLoopDesugar => { + let mut helper = SigDropHelper::new(cx); + helper.find_sig_drop(match_expr) + }, + // MatchSource of TryDesugar or AwaitDesugar is out of scope for this lint + MatchSource::TryDesugar | MatchSource::AwaitDesugar => None, + } + }, _ => None, } } @@ -213,6 +221,19 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> { self.sig_drop_spans.take() } + fn replace_current_sig_drop( + &mut self, + found_span: Span, + is_unit_return_val: bool, + lint_suggestion: LintSuggestion, + ) { + self.current_sig_drop.replace(FoundSigDrop { + found_span, + is_unit_return_val, + lint_suggestion, + }); + } + /// This will try to set the current suggestion (so it can be moved into the suggestions vec /// later). If `allow_move_and_clone` is false, the suggestion *won't* be set -- this gives us /// an opportunity to look for another type in the chain that will be trivially copyable. @@ -229,25 +250,15 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> { // but let's avoid any chance of an ICE if let Some(TypeAndMut { ty, .. }) = ty.builtin_deref(true) { if ty.is_trivially_pure_clone_copy() { - self.current_sig_drop.replace(FoundSigDrop { - found_span: expr.span, - is_unit_return_val: false, - lint_suggestion: LintSuggestion::MoveAndDerefToCopy, - }); + self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndDerefToCopy); } else if allow_move_and_clone { - self.current_sig_drop.replace(FoundSigDrop { - found_span: expr.span, - is_unit_return_val: false, - lint_suggestion: LintSuggestion::MoveAndClone, - }); + self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndClone); } } } else if ty.is_trivially_pure_clone_copy() { - self.current_sig_drop.replace(FoundSigDrop { - found_span: expr.span, - is_unit_return_val: false, - lint_suggestion: LintSuggestion::MoveOnly, - }); + self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveOnly); + } else if allow_move_and_clone { + self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndClone); } } @@ -279,11 +290,7 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> { // If either side had a significant drop, suggest moving the entire scrutinee to avoid // unnecessary copies and to simplify cases where both sides have significant drops. if self.has_significant_drop { - self.current_sig_drop.replace(FoundSigDrop { - found_span: span, - is_unit_return_val, - lint_suggestion: LintSuggestion::MoveOnly, - }); + self.replace_current_sig_drop(span, is_unit_return_val, LintSuggestion::MoveOnly); } self.special_handling_for_binary_op = false; @@ -363,34 +370,34 @@ impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> { } } ExprKind::Box(..) | - ExprKind::Array(..) | - ExprKind::Call(..) | - ExprKind::Unary(..) | - ExprKind::If(..) | - ExprKind::Match(..) | - ExprKind::Field(..) | - ExprKind::Index(..) | - ExprKind::Ret(..) | - ExprKind::Repeat(..) | - ExprKind::Yield(..) | - ExprKind::MethodCall(..) => walk_expr(self, ex), + ExprKind::Array(..) | + ExprKind::Call(..) | + ExprKind::Unary(..) | + ExprKind::If(..) | + ExprKind::Match(..) | + ExprKind::Field(..) | + ExprKind::Index(..) | + ExprKind::Ret(..) | + ExprKind::Repeat(..) | + ExprKind::Yield(..) | + ExprKind::MethodCall(..) => walk_expr(self, ex), ExprKind::AddrOf(_, _, _) | - ExprKind::Block(_, _) | - ExprKind::Break(_, _) | - ExprKind::Cast(_, _) | - // Don't want to check the closure itself, only invocation, which is covered by MethodCall - ExprKind::Closure(_, _, _, _, _) | - ExprKind::ConstBlock(_) | - ExprKind::Continue(_) | - ExprKind::DropTemps(_) | - ExprKind::Err | - ExprKind::InlineAsm(_) | - ExprKind::Let(_) | - ExprKind::Lit(_) | - ExprKind::Loop(_, _, _, _) | - ExprKind::Path(_) | - ExprKind::Struct(_, _, _) | - ExprKind::Type(_, _) => { + ExprKind::Block(_, _) | + ExprKind::Break(_, _) | + ExprKind::Cast(_, _) | + // Don't want to check the closure itself, only invocation, which is covered by MethodCall + ExprKind::Closure(_, _, _, _, _) | + ExprKind::ConstBlock(_) | + ExprKind::Continue(_) | + ExprKind::DropTemps(_) | + ExprKind::Err | + ExprKind::InlineAsm(_) | + ExprKind::Let(_) | + ExprKind::Lit(_) | + ExprKind::Loop(_, _, _, _) | + ExprKind::Path(_) | + ExprKind::Struct(_, _, _) | + ExprKind::Type(_, _) => { return; } } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index adb37cc9d7510..0d61231ada76d 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2073,7 +2073,8 @@ static TEST_ITEM_NAMES_CACHE: SyncOnceCell(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool { let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default())); let mut map: MutexGuard<'_, FxHashMap>> = cache.lock().unwrap(); - match map.entry(module) { + let value = map.entry(module); + match value { Entry::Occupied(entry) => f(entry.get()), Entry::Vacant(entry) => { let mut names = Vec::new(); diff --git a/tests/ui/significant_drop_in_scrutinee.rs b/tests/ui/significant_drop_in_scrutinee.rs index f83a6dd0eb288..4347610f393f3 100644 --- a/tests/ui/significant_drop_in_scrutinee.rs +++ b/tests/ui/significant_drop_in_scrutinee.rs @@ -7,8 +7,10 @@ #![allow(unused_assignments)] #![allow(dead_code)] +use std::num::ParseIntError; use std::ops::Deref; use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::RwLock; use std::sync::{Mutex, MutexGuard}; struct State {} @@ -552,4 +554,41 @@ fn should_not_cause_stack_overflow() { } } +fn should_not_produce_lint_for_try_desugar() -> Result { + // TryDesugar (i.e. using `?` for a Result type) will turn into a match but is out of scope + // for this lint + let rwlock = RwLock::new("1".to_string()); + let result = rwlock.read().unwrap().parse::()?; + println!("{}", result); + rwlock.write().unwrap().push('2'); + Ok(result) +} + +struct ResultReturner { + s: String, +} + +impl ResultReturner { + fn to_number(&self) -> Result { + self.s.parse::() + } +} + +fn should_trigger_lint_for_non_ref_move_and_clone_suggestion() { + let rwlock = RwLock::::new(ResultReturner { s: "1".to_string() }); + match rwlock.read().unwrap().to_number() { + Ok(n) => println!("Converted to number: {}", n), + Err(e) => println!("Could not convert {} to number", e), + }; +} + +fn should_trigger_lint_for_read_write_lock_for_loop() { + // For-in loops desugar to match expressions and are prone to the type of deadlock this lint is + // designed to look for. + let rwlock = RwLock::>::new(vec!["1".to_string()]); + for s in rwlock.read().unwrap().iter() { + println!("{}", s); + } +} + fn main() {} diff --git a/tests/ui/significant_drop_in_scrutinee.stderr b/tests/ui/significant_drop_in_scrutinee.stderr index af16056498505..77942325244d5 100644 --- a/tests/ui/significant_drop_in_scrutinee.stderr +++ b/tests/ui/significant_drop_in_scrutinee.stderr @@ -1,5 +1,5 @@ error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:57:11 + --> $DIR/significant_drop_in_scrutinee.rs:59:11 | LL | match mutex.lock().unwrap().foo() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -12,7 +12,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:130:11 + --> $DIR/significant_drop_in_scrutinee.rs:132:11 | LL | match s.lock_m().get_the_value() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:151:11 + --> $DIR/significant_drop_in_scrutinee.rs:153:11 | LL | match s.lock_m_m().get_the_value() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -36,7 +36,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:199:11 + --> $DIR/significant_drop_in_scrutinee.rs:201:11 | LL | match counter.temp_increment().len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:222:16 + --> $DIR/significant_drop_in_scrutinee.rs:224:16 | LL | match (mutex1.lock().unwrap().s.len(), true) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -60,7 +60,7 @@ LL ~ match (value, true) { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:231:22 + --> $DIR/significant_drop_in_scrutinee.rs:233:22 | LL | match (true, mutex1.lock().unwrap().s.len(), true) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL ~ match (true, value, true) { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:241:16 + --> $DIR/significant_drop_in_scrutinee.rs:243:16 | LL | match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -84,7 +84,7 @@ LL ~ match (value, true, mutex2.lock().unwrap().s.len()) { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:241:54 + --> $DIR/significant_drop_in_scrutinee.rs:243:54 | LL | match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -96,19 +96,19 @@ LL ~ match (mutex1.lock().unwrap().s.len(), true, value) { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:252:15 + --> $DIR/significant_drop_in_scrutinee.rs:254:15 | LL | match mutex3.lock().unwrap().s.as_str() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:262:22 + --> $DIR/significant_drop_in_scrutinee.rs:264:22 | LL | match (true, mutex3.lock().unwrap().s.as_str()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:281:11 + --> $DIR/significant_drop_in_scrutinee.rs:283:11 | LL | match mutex.lock().unwrap().s.len() > 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -120,7 +120,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:288:11 + --> $DIR/significant_drop_in_scrutinee.rs:290:11 | LL | match 1 < mutex.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -132,7 +132,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:306:11 + --> $DIR/significant_drop_in_scrutinee.rs:308:11 | LL | match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -144,7 +144,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:317:11 + --> $DIR/significant_drop_in_scrutinee.rs:319:11 | LL | match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -156,7 +156,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:352:11 + --> $DIR/significant_drop_in_scrutinee.rs:354:11 | LL | match get_mutex_guard().s.len() > 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -168,7 +168,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:369:11 + --> $DIR/significant_drop_in_scrutinee.rs:371:11 | LL | match match i { | ___________^ @@ -191,7 +191,7 @@ LL + .len() ... error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:395:11 + --> $DIR/significant_drop_in_scrutinee.rs:397:11 | LL | match if i > 1 { | ___________^ @@ -214,7 +214,7 @@ LL + .s ... error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:449:11 + --> $DIR/significant_drop_in_scrutinee.rs:451:11 | LL | match s.lock().deref().deref() { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -226,13 +226,13 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:477:11 + --> $DIR/significant_drop_in_scrutinee.rs:479:11 | LL | match s.lock().deref().deref() { | ^^^^^^^^^^^^^^^^^^^^^^^^ error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:496:11 + --> $DIR/significant_drop_in_scrutinee.rs:498:11 | LL | match mutex.lock().unwrap().i = i { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -244,7 +244,7 @@ LL ~ match () { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:502:11 + --> $DIR/significant_drop_in_scrutinee.rs:504:11 | LL | match i = mutex.lock().unwrap().i { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -256,7 +256,7 @@ LL ~ match () { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:508:11 + --> $DIR/significant_drop_in_scrutinee.rs:510:11 | LL | match mutex.lock().unwrap().i += 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -268,7 +268,7 @@ LL ~ match () { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:514:11 + --> $DIR/significant_drop_in_scrutinee.rs:516:11 | LL | match i += mutex.lock().unwrap().i { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -279,5 +279,17 @@ LL ~ i += mutex.lock().unwrap().i; LL ~ match () { | -error: aborting due to 23 previous errors +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:579:11 + | +LL | match rwlock.read().unwrap().to_number() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:589:14 + | +LL | for s in rwlock.read().unwrap().iter() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 25 previous errors From 911eb1f4cde3904e02fa8d2352ac7239b3fa6a32 Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Thu, 19 May 2022 09:03:38 -0400 Subject: [PATCH 39/79] Check `.fixed` paths' existence in `run_ui` --- tests/compile-test.rs | 82 ++++++- tests/ui/bind_instead_of_map_multipart.fixed | 62 +++++ tests/ui/bind_instead_of_map_multipart.rs | 1 + tests/ui/bind_instead_of_map_multipart.stderr | 12 +- tests/ui/crashes/ice-7169.fixed | 12 + tests/ui/crashes/ice-7169.rs | 3 + tests/ui/crashes/ice-7169.stderr | 2 +- tests/ui/crashes/ice-8250.fixed | 7 + tests/ui/crashes/ice-8250.rs | 1 + tests/ui/crashes/ice-8250.stderr | 2 +- tests/ui/crashes/ice-8821.fixed | 9 + tests/ui/crashes/ice-8821.rs | 1 + tests/ui/crashes/ice-8821.stderr | 2 +- tests/ui/implicit_clone.fixed | 118 +++++++++ tests/ui/implicit_clone.rs | 3 +- tests/ui/implicit_clone.stderr | 24 +- tests/ui/issue_2356.fixed | 26 ++ tests/ui/issue_2356.rs | 2 + tests/ui/issue_2356.stderr | 4 +- tests/ui/match_ref_pats.fixed | 118 +++++++++ tests/ui/match_ref_pats.rs | 3 +- tests/ui/match_ref_pats.stderr | 10 +- tests/ui/match_str_case_mismatch.fixed | 186 ++++++++++++++ tests/ui/match_str_case_mismatch.rs | 2 + tests/ui/match_str_case_mismatch.stderr | 14 +- tests/ui/needless_late_init.fixed | 232 ++++++++++++++++++ tests/ui/needless_late_init.rs | 3 +- tests/ui/needless_late_init.stderr | 22 +- tests/ui/nonminimal_bool_methods.fixed | 111 +++++++++ tests/ui/nonminimal_bool_methods.rs | 1 + tests/ui/nonminimal_bool_methods.stderr | 26 +- tests/ui/rc_buffer.fixed | 28 +++ tests/ui/rc_buffer.rs | 2 + tests/ui/rc_buffer.stderr | 16 +- tests/ui/rc_buffer_arc.fixed | 27 ++ tests/ui/rc_buffer_arc.rs | 2 + tests/ui/rc_buffer_arc.stderr | 16 +- tests/ui/suspicious_operation_groupings.fixed | 209 ++++++++++++++++ tests/ui/suspicious_operation_groupings.rs | 3 +- .../ui/suspicious_operation_groupings.stderr | 52 ++-- tests/ui/unicode.fixed | 36 +++ tests/ui/unicode.rs | 1 + tests/ui/unicode.stderr | 14 +- tests/ui/unit_arg_empty_blocks.fixed | 30 +++ tests/ui/unit_arg_empty_blocks.rs | 1 + tests/ui/unit_arg_empty_blocks.stderr | 8 +- tests/ui/unnecessary_cast.fixed | 40 +++ tests/ui/unnecessary_cast.rs | 3 +- tests/ui/unnecessary_cast.stderr | 16 +- 49 files changed, 1478 insertions(+), 127 deletions(-) create mode 100644 tests/ui/bind_instead_of_map_multipart.fixed create mode 100644 tests/ui/crashes/ice-7169.fixed create mode 100644 tests/ui/crashes/ice-8250.fixed create mode 100644 tests/ui/crashes/ice-8821.fixed create mode 100644 tests/ui/implicit_clone.fixed create mode 100644 tests/ui/issue_2356.fixed create mode 100644 tests/ui/match_ref_pats.fixed create mode 100644 tests/ui/match_str_case_mismatch.fixed create mode 100644 tests/ui/needless_late_init.fixed create mode 100644 tests/ui/nonminimal_bool_methods.fixed create mode 100644 tests/ui/rc_buffer.fixed create mode 100644 tests/ui/rc_buffer_arc.fixed create mode 100644 tests/ui/suspicious_operation_groupings.fixed create mode 100644 tests/ui/unicode.fixed create mode 100644 tests/ui/unit_arg_empty_blocks.fixed create mode 100644 tests/ui/unnecessary_cast.fixed diff --git a/tests/compile-test.rs b/tests/compile-test.rs index c9710e3db8e8d..b0bd39801928c 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -1,5 +1,6 @@ #![feature(test)] // compiletest_rs requires this attribute #![feature(once_cell)] +#![feature(is_sorted)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] @@ -162,8 +163,8 @@ fn base_config(test_dir: &str) -> compiletest::Config { } fn run_ui() { - let config = base_config("ui"); - // use tests/clippy.toml + let mut config = base_config("ui"); + config.rustfix_coverage = true; let _g = VarGuard::set("CARGO_MANIFEST_DIR", fs::canonicalize("tests").unwrap()); let _threads = VarGuard::set( "RUST_TEST_THREADS", @@ -175,6 +176,7 @@ fn run_ui() { }), ); compiletest::run_tests(&config); + check_rustfix_coverage(); } fn run_internal_tests() { @@ -337,6 +339,82 @@ fn compile_test() { run_internal_tests(); } +const RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS: &[&str] = &[ + "assign_ops2.rs", + "cast_size_32bit.rs", + "char_lit_as_u8.rs", + "cmp_owned/without_suggestion.rs", + "crashes/ice-6250.rs", + "crashes/ice-6251.rs", + "dbg_macro.rs", + "deref_addrof_double_trigger.rs", + "doc/unbalanced_ticks.rs", + "eprint_with_newline.rs", + "explicit_counter_loop.rs", + "iter_skip_next_unfixable.rs", + "let_and_return.rs", + "literals.rs", + "map_flatten.rs", + "map_unwrap_or.rs", + "match_bool.rs", + "mem_replace_macro.rs", + "needless_arbitrary_self_type_unfixable.rs", + "needless_borrow_pat.rs", + "needless_for_each_unfixable.rs", + "nonminimal_bool.rs", + "print_literal.rs", + "print_with_newline.rs", + "redundant_static_lifetimes_multiple.rs", + "ref_binding_to_reference.rs", + "repl_uninit.rs", + "result_map_unit_fn_unfixable.rs", + "search_is_some.rs", + "single_component_path_imports_nested_first.rs", + "string_add.rs", + "toplevel_ref_arg_non_rustfix.rs", + "unit_arg.rs", + "unnecessary_clone.rs", + "unnecessary_lazy_eval_unfixable.rs", + "write_literal.rs", + "write_literal_2.rs", + "write_with_newline.rs", +]; + +fn check_rustfix_coverage() { + let missing_coverage_path = Path::new("target/debug/test/ui/rustfix_missing_coverage.txt"); + + if let Ok(missing_coverage_contents) = std::fs::read_to_string(missing_coverage_path) { + assert!(RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS.is_sorted()); + + for rs_path in missing_coverage_contents.lines() { + let filename = rs_path.strip_prefix("tests/ui/").unwrap(); + assert!( + RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS.binary_search(&filename).is_ok(), + "`{}` runs `MachineApplicable` diagnostics but is missing a `run-rustfix` annotation", + rs_path, + ); + } + } +} + +#[test] +fn rustfix_coverage_known_exceptions_accuracy() { + for filename in RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS { + let rs_path = Path::new("tests/ui").join(filename); + assert!( + rs_path.exists(), + "`{}` does not exists", + rs_path.strip_prefix(env!("CARGO_MANIFEST_DIR")).unwrap().display() + ); + let fixed_path = rs_path.with_extension("fixed"); + assert!( + !fixed_path.exists(), + "`{}` exists", + fixed_path.strip_prefix(env!("CARGO_MANIFEST_DIR")).unwrap().display() + ); + } +} + /// Restores an env var on drop #[must_use] struct VarGuard { diff --git a/tests/ui/bind_instead_of_map_multipart.fixed b/tests/ui/bind_instead_of_map_multipart.fixed new file mode 100644 index 0000000000000..e1589843226c4 --- /dev/null +++ b/tests/ui/bind_instead_of_map_multipart.fixed @@ -0,0 +1,62 @@ +// run-rustfix +#![deny(clippy::bind_instead_of_map)] +#![allow(clippy::blocks_in_if_conditions)] + +pub fn main() { + let _ = Some("42").map(|s| if s.len() < 42 { 0 } else { s.len() }); + let _ = Some("42").and_then(|s| if s.len() < 42 { None } else { Some(s.len()) }); + + let _ = Ok::<_, ()>("42").map(|s| if s.len() < 42 { 0 } else { s.len() }); + let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Err(()) } else { Ok(s.len()) }); + + let _ = Err::<(), _>("42").map_err(|s| if s.len() < 42 { s.len() + 20 } else { s.len() }); + let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Ok(()) } else { Err(s.len()) }); + + hard_example(); + macro_example(); +} + +fn hard_example() { + Some("42").map(|s| { + if { + if s == "43" { + return 43; + } + s == "42" + } { + return 45; + } + match s.len() { + 10 => 2, + 20 => { + if foo() { + return { + if foo() { + return 20; + } + println!("foo"); + 3 + }; + } + 20 + }, + 40 => 30, + _ => 1, + } + }); +} + +fn foo() -> bool { + true +} + +macro_rules! m { + () => { + Some(10) + }; +} + +fn macro_example() { + let _ = Some("").and_then(|s| if s.len() == 20 { m!() } else { Some(20) }); + let _ = Some("").map(|s| if s.len() == 20 { m!() } else { Some(20) }); +} diff --git a/tests/ui/bind_instead_of_map_multipart.rs b/tests/ui/bind_instead_of_map_multipart.rs index 91d9d11e3c110..49944403f6ddd 100644 --- a/tests/ui/bind_instead_of_map_multipart.rs +++ b/tests/ui/bind_instead_of_map_multipart.rs @@ -1,3 +1,4 @@ +// run-rustfix #![deny(clippy::bind_instead_of_map)] #![allow(clippy::blocks_in_if_conditions)] diff --git a/tests/ui/bind_instead_of_map_multipart.stderr b/tests/ui/bind_instead_of_map_multipart.stderr index e4f605a4de305..f822b6f49fa35 100644 --- a/tests/ui/bind_instead_of_map_multipart.stderr +++ b/tests/ui/bind_instead_of_map_multipart.stderr @@ -1,11 +1,11 @@ error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` - --> $DIR/bind_instead_of_map_multipart.rs:5:13 + --> $DIR/bind_instead_of_map_multipart.rs:6:13 | LL | let _ = Some("42").and_then(|s| if s.len() < 42 { Some(0) } else { Some(s.len()) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/bind_instead_of_map_multipart.rs:1:9 + --> $DIR/bind_instead_of_map_multipart.rs:2:9 | LL | #![deny(clippy::bind_instead_of_map)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | let _ = Some("42").map(|s| if s.len() < 42 { 0 } else { s.len() }); | ~~~ ~ ~~~~~~~ error: using `Result.and_then(|x| Ok(y))`, which is more succinctly expressed as `map(|x| y)` - --> $DIR/bind_instead_of_map_multipart.rs:8:13 + --> $DIR/bind_instead_of_map_multipart.rs:9:13 | LL | let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Ok(0) } else { Ok(s.len()) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -26,7 +26,7 @@ LL | let _ = Ok::<_, ()>("42").map(|s| if s.len() < 42 { 0 } else { s.len() | ~~~ ~ ~~~~~~~ error: using `Result.or_else(|x| Err(y))`, which is more succinctly expressed as `map_err(|x| y)` - --> $DIR/bind_instead_of_map_multipart.rs:11:13 + --> $DIR/bind_instead_of_map_multipart.rs:12:13 | LL | let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Err(s.len() + 20) } else { Err(s.len()) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL | let _ = Err::<(), _>("42").map_err(|s| if s.len() < 42 { s.len() + 20 } | ~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~ error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` - --> $DIR/bind_instead_of_map_multipart.rs:19:5 + --> $DIR/bind_instead_of_map_multipart.rs:20:5 | LL | / Some("42").and_then(|s| { LL | | if { @@ -59,7 +59,7 @@ LL | s == "42" ... error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` - --> $DIR/bind_instead_of_map_multipart.rs:60:13 + --> $DIR/bind_instead_of_map_multipart.rs:61:13 | LL | let _ = Some("").and_then(|s| if s.len() == 20 { Some(m!()) } else { Some(Some(20)) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/crashes/ice-7169.fixed b/tests/ui/crashes/ice-7169.fixed new file mode 100644 index 0000000000000..b36fb49ee7493 --- /dev/null +++ b/tests/ui/crashes/ice-7169.fixed @@ -0,0 +1,12 @@ +// run-rustfix +#![allow(dead_code)] + +#[derive(Default)] +struct A { + a: Vec>, + b: T, +} + +fn main() { + if Ok::<_, ()>(A::::default()).is_ok() {} +} diff --git a/tests/ui/crashes/ice-7169.rs b/tests/ui/crashes/ice-7169.rs index 82095febc194a..1769a96bd2f60 100644 --- a/tests/ui/crashes/ice-7169.rs +++ b/tests/ui/crashes/ice-7169.rs @@ -1,3 +1,6 @@ +// run-rustfix +#![allow(dead_code)] + #[derive(Default)] struct A { a: Vec>, diff --git a/tests/ui/crashes/ice-7169.stderr b/tests/ui/crashes/ice-7169.stderr index 5a9cd32380a19..e7d0671386b6f 100644 --- a/tests/ui/crashes/ice-7169.stderr +++ b/tests/ui/crashes/ice-7169.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_ok()` - --> $DIR/ice-7169.rs:8:12 + --> $DIR/ice-7169.rs:11:12 | LL | if let Ok(_) = Ok::<_, ()>(A::::default()) {} | -------^^^^^-------------------------------------- help: try this: `if Ok::<_, ()>(A::::default()).is_ok()` diff --git a/tests/ui/crashes/ice-8250.fixed b/tests/ui/crashes/ice-8250.fixed new file mode 100644 index 0000000000000..950dc0b4d5789 --- /dev/null +++ b/tests/ui/crashes/ice-8250.fixed @@ -0,0 +1,7 @@ +// run-rustfix +fn _f(s: &str) -> Option<()> { + let _ = s[1..].split('.').next()?; + Some(()) +} + +fn main() {} diff --git a/tests/ui/crashes/ice-8250.rs b/tests/ui/crashes/ice-8250.rs index d9a5ee1162a49..d3183d88faee7 100644 --- a/tests/ui/crashes/ice-8250.rs +++ b/tests/ui/crashes/ice-8250.rs @@ -1,3 +1,4 @@ +// run-rustfix fn _f(s: &str) -> Option<()> { let _ = s[1..].splitn(2, '.').next()?; Some(()) diff --git a/tests/ui/crashes/ice-8250.stderr b/tests/ui/crashes/ice-8250.stderr index 8ed8f3b3a0642..46d5aa7a2fc03 100644 --- a/tests/ui/crashes/ice-8250.stderr +++ b/tests/ui/crashes/ice-8250.stderr @@ -1,5 +1,5 @@ error: unnecessary use of `splitn` - --> $DIR/ice-8250.rs:2:13 + --> $DIR/ice-8250.rs:3:13 | LL | let _ = s[1..].splitn(2, '.').next()?; | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `s[1..].split('.')` diff --git a/tests/ui/crashes/ice-8821.fixed b/tests/ui/crashes/ice-8821.fixed new file mode 100644 index 0000000000000..046b4522ec1a7 --- /dev/null +++ b/tests/ui/crashes/ice-8821.fixed @@ -0,0 +1,9 @@ +// run-rustfix +#![warn(clippy::let_unit_value)] + +fn f() {} +static FN: fn() = f; + +fn main() { + FN(); +} diff --git a/tests/ui/crashes/ice-8821.rs b/tests/ui/crashes/ice-8821.rs index fb87b79aeed87..941225e9d1e51 100644 --- a/tests/ui/crashes/ice-8821.rs +++ b/tests/ui/crashes/ice-8821.rs @@ -1,3 +1,4 @@ +// run-rustfix #![warn(clippy::let_unit_value)] fn f() {} diff --git a/tests/ui/crashes/ice-8821.stderr b/tests/ui/crashes/ice-8821.stderr index 486096e0a06dd..aee13f00ca02c 100644 --- a/tests/ui/crashes/ice-8821.stderr +++ b/tests/ui/crashes/ice-8821.stderr @@ -1,5 +1,5 @@ error: this let-binding has unit value - --> $DIR/ice-8821.rs:7:5 + --> $DIR/ice-8821.rs:8:5 | LL | let _: () = FN(); | ^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `FN();` diff --git a/tests/ui/implicit_clone.fixed b/tests/ui/implicit_clone.fixed new file mode 100644 index 0000000000000..33770fc2a2cf9 --- /dev/null +++ b/tests/ui/implicit_clone.fixed @@ -0,0 +1,118 @@ +// run-rustfix +#![warn(clippy::implicit_clone)] +#![allow(clippy::clone_on_copy, clippy::redundant_clone)] +use std::borrow::Borrow; +use std::ffi::{OsStr, OsString}; +use std::path::PathBuf; + +fn return_owned_from_slice(slice: &[u32]) -> Vec { + slice.to_owned() +} + +pub fn own_same(v: T) -> T +where + T: ToOwned, +{ + v.to_owned() +} + +pub fn own_same_from_ref(v: &T) -> T +where + T: ToOwned, +{ + v.to_owned() +} + +pub fn own_different(v: T) -> U +where + T: ToOwned, +{ + v.to_owned() +} + +#[derive(Copy, Clone)] +struct Kitten; +impl Kitten { + // badly named method + fn to_vec(self) -> Kitten { + Kitten {} + } +} +impl Borrow for Kitten { + fn borrow(&self) -> &BorrowedKitten { + static VALUE: BorrowedKitten = BorrowedKitten {}; + &VALUE + } +} + +struct BorrowedKitten; +impl ToOwned for BorrowedKitten { + type Owned = Kitten; + fn to_owned(&self) -> Kitten { + Kitten {} + } +} + +mod weird { + #[allow(clippy::ptr_arg)] + pub fn to_vec(v: &Vec) -> Vec { + v.clone() + } +} + +fn main() { + let vec = vec![5]; + let _ = return_owned_from_slice(&vec); + let _ = vec.clone(); + let _ = vec.clone(); + + let vec_ref = &vec; + let _ = return_owned_from_slice(vec_ref); + let _ = vec_ref.clone(); + let _ = vec_ref.clone(); + + // we expect no lint for this + let _ = weird::to_vec(&vec); + + // we expect no lints for this + let slice: &[u32] = &[1, 2, 3, 4, 5]; + let _ = return_owned_from_slice(slice); + let _ = slice.to_owned(); + let _ = slice.to_vec(); + + let str = "hello world".to_string(); + let _ = str.clone(); + + // testing w/ an arbitrary type + let kitten = Kitten {}; + let _ = kitten.clone(); + let _ = own_same_from_ref(&kitten); + // this shouln't lint + let _ = kitten.to_vec(); + + // we expect no lints for this + let borrowed = BorrowedKitten {}; + let _ = borrowed.to_owned(); + + let pathbuf = PathBuf::new(); + let _ = pathbuf.clone(); + let _ = pathbuf.clone(); + + let os_string = OsString::from("foo"); + let _ = os_string.clone(); + let _ = os_string.clone(); + + // we expect no lints for this + let os_str = OsStr::new("foo"); + let _ = os_str.to_owned(); + let _ = os_str.to_os_string(); + + // issue #8227 + let pathbuf_ref = &pathbuf; + let pathbuf_ref = &pathbuf_ref; + let _ = pathbuf_ref.to_owned(); // Don't lint. Returns `&PathBuf` + let _ = (*pathbuf_ref).clone(); + let pathbuf_ref = &pathbuf_ref; + let _ = pathbuf_ref.to_owned(); // Don't lint. Returns `&&PathBuf` + let _ = (**pathbuf_ref).clone(); +} diff --git a/tests/ui/implicit_clone.rs b/tests/ui/implicit_clone.rs index 2549c9f32f904..fc896525bd270 100644 --- a/tests/ui/implicit_clone.rs +++ b/tests/ui/implicit_clone.rs @@ -1,5 +1,6 @@ +// run-rustfix #![warn(clippy::implicit_clone)] -#![allow(clippy::redundant_clone)] +#![allow(clippy::clone_on_copy, clippy::redundant_clone)] use std::borrow::Borrow; use std::ffi::{OsStr, OsString}; use std::path::PathBuf; diff --git a/tests/ui/implicit_clone.stderr b/tests/ui/implicit_clone.stderr index 0f4124241907f..92c1aa58affb2 100644 --- a/tests/ui/implicit_clone.stderr +++ b/tests/ui/implicit_clone.stderr @@ -1,5 +1,5 @@ error: implicitly cloning a `Vec` by calling `to_owned` on its dereferenced type - --> $DIR/implicit_clone.rs:65:13 + --> $DIR/implicit_clone.rs:66:13 | LL | let _ = vec.to_owned(); | ^^^^^^^^^^^^^^ help: consider using: `vec.clone()` @@ -7,67 +7,67 @@ LL | let _ = vec.to_owned(); = note: `-D clippy::implicit-clone` implied by `-D warnings` error: implicitly cloning a `Vec` by calling `to_vec` on its dereferenced type - --> $DIR/implicit_clone.rs:66:13 + --> $DIR/implicit_clone.rs:67:13 | LL | let _ = vec.to_vec(); | ^^^^^^^^^^^^ help: consider using: `vec.clone()` error: implicitly cloning a `Vec` by calling `to_owned` on its dereferenced type - --> $DIR/implicit_clone.rs:70:13 + --> $DIR/implicit_clone.rs:71:13 | LL | let _ = vec_ref.to_owned(); | ^^^^^^^^^^^^^^^^^^ help: consider using: `vec_ref.clone()` error: implicitly cloning a `Vec` by calling `to_vec` on its dereferenced type - --> $DIR/implicit_clone.rs:71:13 + --> $DIR/implicit_clone.rs:72:13 | LL | let _ = vec_ref.to_vec(); | ^^^^^^^^^^^^^^^^ help: consider using: `vec_ref.clone()` error: implicitly cloning a `String` by calling `to_owned` on its dereferenced type - --> $DIR/implicit_clone.rs:83:13 + --> $DIR/implicit_clone.rs:84:13 | LL | let _ = str.to_owned(); | ^^^^^^^^^^^^^^ help: consider using: `str.clone()` error: implicitly cloning a `Kitten` by calling `to_owned` on its dereferenced type - --> $DIR/implicit_clone.rs:87:13 + --> $DIR/implicit_clone.rs:88:13 | LL | let _ = kitten.to_owned(); | ^^^^^^^^^^^^^^^^^ help: consider using: `kitten.clone()` error: implicitly cloning a `PathBuf` by calling `to_owned` on its dereferenced type - --> $DIR/implicit_clone.rs:97:13 + --> $DIR/implicit_clone.rs:98:13 | LL | let _ = pathbuf.to_owned(); | ^^^^^^^^^^^^^^^^^^ help: consider using: `pathbuf.clone()` error: implicitly cloning a `PathBuf` by calling `to_path_buf` on its dereferenced type - --> $DIR/implicit_clone.rs:98:13 + --> $DIR/implicit_clone.rs:99:13 | LL | let _ = pathbuf.to_path_buf(); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `pathbuf.clone()` error: implicitly cloning a `OsString` by calling `to_owned` on its dereferenced type - --> $DIR/implicit_clone.rs:101:13 + --> $DIR/implicit_clone.rs:102:13 | LL | let _ = os_string.to_owned(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `os_string.clone()` error: implicitly cloning a `OsString` by calling `to_os_string` on its dereferenced type - --> $DIR/implicit_clone.rs:102:13 + --> $DIR/implicit_clone.rs:103:13 | LL | let _ = os_string.to_os_string(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `os_string.clone()` error: implicitly cloning a `PathBuf` by calling `to_path_buf` on its dereferenced type - --> $DIR/implicit_clone.rs:113:13 + --> $DIR/implicit_clone.rs:114:13 | LL | let _ = pathbuf_ref.to_path_buf(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(*pathbuf_ref).clone()` error: implicitly cloning a `PathBuf` by calling `to_path_buf` on its dereferenced type - --> $DIR/implicit_clone.rs:116:13 + --> $DIR/implicit_clone.rs:117:13 | LL | let _ = pathbuf_ref.to_path_buf(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(**pathbuf_ref).clone()` diff --git a/tests/ui/issue_2356.fixed b/tests/ui/issue_2356.fixed new file mode 100644 index 0000000000000..942e99fa8787b --- /dev/null +++ b/tests/ui/issue_2356.fixed @@ -0,0 +1,26 @@ +// run-rustfix +#![deny(clippy::while_let_on_iterator)] +#![allow(unused_mut)] + +use std::iter::Iterator; + +struct Foo; + +impl Foo { + fn foo1>(mut it: I) { + while let Some(_) = it.next() { + println!("{:?}", it.size_hint()); + } + } + + fn foo2>(mut it: I) { + for e in it { + println!("{:?}", e); + } + } +} + +fn main() { + Foo::foo1(vec![].into_iter()); + Foo::foo2(vec![].into_iter()); +} diff --git a/tests/ui/issue_2356.rs b/tests/ui/issue_2356.rs index da580a1839a17..b000234ea5966 100644 --- a/tests/ui/issue_2356.rs +++ b/tests/ui/issue_2356.rs @@ -1,4 +1,6 @@ +// run-rustfix #![deny(clippy::while_let_on_iterator)] +#![allow(unused_mut)] use std::iter::Iterator; diff --git a/tests/ui/issue_2356.stderr b/tests/ui/issue_2356.stderr index 51b872e21c085..4e3ff7522e0bb 100644 --- a/tests/ui/issue_2356.stderr +++ b/tests/ui/issue_2356.stderr @@ -1,11 +1,11 @@ error: this loop could be written as a `for` loop - --> $DIR/issue_2356.rs:15:9 + --> $DIR/issue_2356.rs:17:9 | LL | while let Some(e) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for e in it` | note: the lint level is defined here - --> $DIR/issue_2356.rs:1:9 + --> $DIR/issue_2356.rs:2:9 | LL | #![deny(clippy::while_let_on_iterator)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/match_ref_pats.fixed b/tests/ui/match_ref_pats.fixed new file mode 100644 index 0000000000000..1b6c2d9241218 --- /dev/null +++ b/tests/ui/match_ref_pats.fixed @@ -0,0 +1,118 @@ +// run-rustfix +#![warn(clippy::match_ref_pats)] +#![allow(dead_code, unused_variables, clippy::equatable_if_let, clippy::enum_variant_names)] + +fn ref_pats() { + { + let v = &Some(0); + match *v { + Some(v) => println!("{:?}", v), + None => println!("none"), + } + match v { + // This doesn't trigger; we have a different pattern. + &Some(v) => println!("some"), + other => println!("other"), + } + } + let tup = &(1, 2); + match tup { + &(v, 1) => println!("{}", v), + _ => println!("none"), + } + // Special case: using `&` both in expr and pats. + let w = Some(0); + match w { + Some(v) => println!("{:?}", v), + None => println!("none"), + } + // False positive: only wildcard pattern. + let w = Some(0); + #[allow(clippy::match_single_binding)] + match w { + _ => println!("none"), + } + + let a = &Some(0); + if a.is_none() { + println!("none"); + } + + let b = Some(0); + if b.is_none() { + println!("none"); + } +} + +mod ice_3719 { + macro_rules! foo_variant( + ($idx:expr) => (Foo::get($idx).unwrap()) + ); + + enum Foo { + A, + B, + } + + impl Foo { + fn get(idx: u8) -> Option<&'static Self> { + match idx { + 0 => Some(&Foo::A), + 1 => Some(&Foo::B), + _ => None, + } + } + } + + fn ice_3719() { + // ICE #3719 + match foo_variant!(0) { + &Foo::A => println!("A"), + _ => println!("Wild"), + } + } +} + +mod issue_7740 { + macro_rules! foobar_variant( + ($idx:expr) => (FooBar::get($idx).unwrap()) + ); + + enum FooBar { + Foo, + Bar, + FooBar, + BarFoo, + } + + impl FooBar { + fn get(idx: u8) -> Option<&'static Self> { + match idx { + 0 => Some(&FooBar::Foo), + 1 => Some(&FooBar::Bar), + 2 => Some(&FooBar::FooBar), + 3 => Some(&FooBar::BarFoo), + _ => None, + } + } + } + + fn issue_7740() { + // Issue #7740 + match *foobar_variant!(0) { + FooBar::Foo => println!("Foo"), + FooBar::Bar => println!("Bar"), + FooBar::FooBar => println!("FooBar"), + _ => println!("Wild"), + } + + // This shouldn't trigger + if let &FooBar::BarFoo = foobar_variant!(3) { + println!("BarFoo"); + } else { + println!("Wild"); + } + } +} + +fn main() {} diff --git a/tests/ui/match_ref_pats.rs b/tests/ui/match_ref_pats.rs index 7e3674ab8c9f2..68dfac4e2e978 100644 --- a/tests/ui/match_ref_pats.rs +++ b/tests/ui/match_ref_pats.rs @@ -1,5 +1,6 @@ +// run-rustfix #![warn(clippy::match_ref_pats)] -#![allow(clippy::equatable_if_let, clippy::enum_variant_names)] +#![allow(dead_code, unused_variables, clippy::equatable_if_let, clippy::enum_variant_names)] fn ref_pats() { { diff --git a/tests/ui/match_ref_pats.stderr b/tests/ui/match_ref_pats.stderr index 901820077e20e..353f7399d9c27 100644 --- a/tests/ui/match_ref_pats.stderr +++ b/tests/ui/match_ref_pats.stderr @@ -1,5 +1,5 @@ error: you don't need to add `&` to all patterns - --> $DIR/match_ref_pats.rs:7:9 + --> $DIR/match_ref_pats.rs:8:9 | LL | / match v { LL | | &Some(v) => println!("{:?}", v), @@ -16,7 +16,7 @@ LL ~ None => println!("none"), | error: you don't need to add `&` to both the expression and the patterns - --> $DIR/match_ref_pats.rs:24:5 + --> $DIR/match_ref_pats.rs:25:5 | LL | / match &w { LL | | &Some(v) => println!("{:?}", v), @@ -32,7 +32,7 @@ LL ~ None => println!("none"), | error: redundant pattern matching, consider using `is_none()` - --> $DIR/match_ref_pats.rs:36:12 + --> $DIR/match_ref_pats.rs:37:12 | LL | if let &None = a { | -------^^^^^---- help: try this: `if a.is_none()` @@ -40,13 +40,13 @@ LL | if let &None = a { = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_none()` - --> $DIR/match_ref_pats.rs:41:12 + --> $DIR/match_ref_pats.rs:42:12 | LL | if let &None = &b { | -------^^^^^----- help: try this: `if b.is_none()` error: you don't need to add `&` to all patterns - --> $DIR/match_ref_pats.rs:101:9 + --> $DIR/match_ref_pats.rs:102:9 | LL | / match foobar_variant!(0) { LL | | &FooBar::Foo => println!("Foo"), diff --git a/tests/ui/match_str_case_mismatch.fixed b/tests/ui/match_str_case_mismatch.fixed new file mode 100644 index 0000000000000..e436bcf495fd2 --- /dev/null +++ b/tests/ui/match_str_case_mismatch.fixed @@ -0,0 +1,186 @@ +// run-rustfix +#![warn(clippy::match_str_case_mismatch)] +#![allow(dead_code)] + +// Valid + +fn as_str_match() { + let var = "BAR"; + + match var.to_ascii_lowercase().as_str() { + "foo" => {}, + "bar" => {}, + _ => {}, + } +} + +fn non_alphabetic() { + let var = "~!@#$%^&*()-_=+FOO"; + + match var.to_ascii_lowercase().as_str() { + "1234567890" => {}, + "~!@#$%^&*()-_=+foo" => {}, + "\n\r\t\x7F" => {}, + _ => {}, + } +} + +fn unicode_cased() { + let var = "ВОДЫ"; + + match var.to_lowercase().as_str() { + "水" => {}, + "νερό" => {}, + "воды" => {}, + "물" => {}, + _ => {}, + } +} + +fn titlecase() { + let var = "BarDz"; + + match var.to_lowercase().as_str() { + "foolj" => {}, + "bardz" => {}, + _ => {}, + } +} + +fn no_case_equivalent() { + let var = "barʁ"; + + match var.to_uppercase().as_str() { + "FOOɕ" => {}, + "BARʁ" => {}, + _ => {}, + } +} + +fn addrof_unary_match() { + let var = "BAR"; + + match &*var.to_ascii_lowercase() { + "foo" => {}, + "bar" => {}, + _ => {}, + } +} + +fn alternating_chain() { + let var = "BAR"; + + match &*var + .to_ascii_lowercase() + .to_uppercase() + .to_lowercase() + .to_ascii_uppercase() + { + "FOO" => {}, + "BAR" => {}, + _ => {}, + } +} + +fn unrelated_method() { + struct Item { + a: String, + } + + impl Item { + #[allow(clippy::wrong_self_convention)] + fn to_lowercase(self) -> String { + self.a + } + } + + let item = Item { a: String::from("BAR") }; + + match &*item.to_lowercase() { + "FOO" => {}, + "BAR" => {}, + _ => {}, + } +} + +// Invalid + +fn as_str_match_mismatch() { + let var = "BAR"; + + match var.to_ascii_lowercase().as_str() { + "foo" => {}, + "bar" => {}, + _ => {}, + } +} + +fn non_alphabetic_mismatch() { + let var = "~!@#$%^&*()-_=+FOO"; + + match var.to_ascii_lowercase().as_str() { + "1234567890" => {}, + "~!@#$%^&*()-_=+foo" => {}, + "\n\r\t\x7F" => {}, + _ => {}, + } +} + +fn unicode_cased_mismatch() { + let var = "ВОДЫ"; + + match var.to_lowercase().as_str() { + "水" => {}, + "νερό" => {}, + "воды" => {}, + "물" => {}, + _ => {}, + } +} + +fn titlecase_mismatch() { + let var = "BarDz"; + + match var.to_lowercase().as_str() { + "foolj" => {}, + "bardz" => {}, + _ => {}, + } +} + +fn no_case_equivalent_mismatch() { + let var = "barʁ"; + + match var.to_uppercase().as_str() { + "FOOɕ" => {}, + "BARʁ" => {}, + _ => {}, + } +} + +fn addrof_unary_match_mismatch() { + let var = "BAR"; + + match &*var.to_ascii_lowercase() { + "foo" => {}, + "bar" => {}, + _ => {}, + } +} + +fn alternating_chain_mismatch() { + let var = "BAR"; + + match &*var + .to_ascii_lowercase() + .to_uppercase() + .to_lowercase() + .to_ascii_uppercase() + { + "FOO" => {}, + "BAR" => {}, + _ => {}, + } +} + +fn main() {} diff --git a/tests/ui/match_str_case_mismatch.rs b/tests/ui/match_str_case_mismatch.rs index ac555c87d83b2..92e2a000ade25 100644 --- a/tests/ui/match_str_case_mismatch.rs +++ b/tests/ui/match_str_case_mismatch.rs @@ -1,4 +1,6 @@ +// run-rustfix #![warn(clippy::match_str_case_mismatch)] +#![allow(dead_code)] // Valid diff --git a/tests/ui/match_str_case_mismatch.stderr b/tests/ui/match_str_case_mismatch.stderr index 92baa40ef28f0..197520a3d6081 100644 --- a/tests/ui/match_str_case_mismatch.stderr +++ b/tests/ui/match_str_case_mismatch.stderr @@ -1,5 +1,5 @@ error: this `match` arm has a differing case than its expression - --> $DIR/match_str_case_mismatch.rs:111:9 + --> $DIR/match_str_case_mismatch.rs:113:9 | LL | "Bar" => {}, | ^^^^^ @@ -11,7 +11,7 @@ LL | "bar" => {}, | ~~~~~ error: this `match` arm has a differing case than its expression - --> $DIR/match_str_case_mismatch.rs:121:9 + --> $DIR/match_str_case_mismatch.rs:123:9 | LL | "~!@#$%^&*()-_=+Foo" => {}, | ^^^^^^^^^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | "~!@#$%^&*()-_=+foo" => {}, | ~~~~~~~~~~~~~~~~~~~~ error: this `match` arm has a differing case than its expression - --> $DIR/match_str_case_mismatch.rs:133:9 + --> $DIR/match_str_case_mismatch.rs:135:9 | LL | "Воды" => {}, | ^^^^^^ @@ -33,7 +33,7 @@ LL | "воды" => {}, | ~~~~~~ error: this `match` arm has a differing case than its expression - --> $DIR/match_str_case_mismatch.rs:144:9 + --> $DIR/match_str_case_mismatch.rs:146:9 | LL | "barDz" => {}, | ^^^^^^ @@ -44,7 +44,7 @@ LL | "bardz" => {}, | ~~~~~~ error: this `match` arm has a differing case than its expression - --> $DIR/match_str_case_mismatch.rs:154:9 + --> $DIR/match_str_case_mismatch.rs:156:9 | LL | "bARʁ" => {}, | ^^^^^^ @@ -55,7 +55,7 @@ LL | "BARʁ" => {}, | ~~~~~~ error: this `match` arm has a differing case than its expression - --> $DIR/match_str_case_mismatch.rs:164:9 + --> $DIR/match_str_case_mismatch.rs:166:9 | LL | "Bar" => {}, | ^^^^^ @@ -66,7 +66,7 @@ LL | "bar" => {}, | ~~~~~ error: this `match` arm has a differing case than its expression - --> $DIR/match_str_case_mismatch.rs:179:9 + --> $DIR/match_str_case_mismatch.rs:181:9 | LL | "bAR" => {}, | ^^^^^ diff --git a/tests/ui/needless_late_init.fixed b/tests/ui/needless_late_init.fixed new file mode 100644 index 0000000000000..9666b2c84460a --- /dev/null +++ b/tests/ui/needless_late_init.fixed @@ -0,0 +1,232 @@ +// run-rustfix +#![feature(let_chains)] +#![allow(unused, clippy::nonminimal_bool, clippy::let_unit_value, clippy::let_and_return)] + +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; +use std::rc::Rc; + +struct SignificantDrop; +impl std::ops::Drop for SignificantDrop { + fn drop(&mut self) { + println!("dropped"); + } +} + +fn main() { + + let n = 1; + let a = match n { + 1 => "one", + _ => { + "two" + }, + }; + + + let b = if n == 3 { + "four" + } else { + "five" + }; + + + let d = if true { + let temp = 5; + temp + } else { + 15 + }; + + + let e = if true { + format!("{} {}", a, b) + } else { + format!("{}", n) + }; + + + let f = match 1 { + 1 => "three", + _ => return, + }; // has semi + + + let g: usize = if true { + 5 + } else { + panic!(); + }; + + // Drop order only matters if both are significant + + let y = SignificantDrop; + let x = 1; + + + let y = 1; + let x = SignificantDrop; + + + // types that should be considered insignificant + let y = 1; + let y = "2"; + let y = String::new(); + let y = vec![3.0]; + let y = HashMap::::new(); + let y = BTreeMap::::new(); + let y = HashSet::::new(); + let y = BTreeSet::::new(); + let y = Box::new(4); + let x = SignificantDrop; +} + +async fn in_async() -> &'static str { + async fn f() -> &'static str { + "one" + } + + + let n = 1; + let a = match n { + 1 => f().await, + _ => { + "two" + }, + }; + + a +} + +const fn in_const() -> &'static str { + const fn f() -> &'static str { + "one" + } + + + let n = 1; + let a = match n { + 1 => f(), + _ => { + "two" + }, + }; + + a +} + +fn does_not_lint() { + let z; + if false { + z = 1; + } + + let x; + let y; + if true { + x = 1; + } else { + y = 1; + } + + let mut x; + if true { + x = 5; + x = 10 / x; + } else { + x = 2; + } + + let x; + let _ = match 1 { + 1 => x = 10, + _ => x = 20, + }; + + // using tuples would be possible, but not always preferable + let x; + let y; + if true { + x = 1; + y = 2; + } else { + x = 3; + y = 4; + } + + // could match with a smarter heuristic to avoid multiple assignments + let x; + if true { + let mut y = 5; + y = 6; + x = y; + } else { + x = 2; + } + + let (x, y); + if true { + x = 1; + } else { + x = 2; + } + y = 3; + + macro_rules! assign { + ($i:ident) => { + $i = 1; + }; + } + let x; + assign!(x); + + let x; + if true { + assign!(x); + } else { + x = 2; + } + + macro_rules! in_macro { + () => { + let x; + x = 1; + + let x; + if true { + x = 1; + } else { + x = 2; + } + }; + } + in_macro!(); + + // ignore if-lets - https://github.com/rust-lang/rust-clippy/issues/8613 + let x; + if let Some(n) = Some("v") { + x = 1; + } else { + x = 2; + } + + let x; + if true && let Some(n) = Some("let chains too") { + x = 1; + } else { + x = 2; + } + + // ignore mut bindings + // https://github.com/shepmaster/twox-hash/blob/b169c16d86eb8ea4a296b0acb9d00ca7e3c3005f/src/sixty_four.rs#L88-L93 + // https://github.com/dtolnay/thiserror/blob/21c26903e29cb92ba1a7ff11e82ae2001646b60d/tests/test_generics.rs#L91-L100 + let mut x: usize; + x = 1; + x = 2; + x = 3; + + // should not move the declaration if `x` has a significant drop, and there + // is another binding with a significant drop between it and the first usage + let x; + let y = SignificantDrop; + x = SignificantDrop; +} diff --git a/tests/ui/needless_late_init.rs b/tests/ui/needless_late_init.rs index 54e66b391b8d8..c0d11d306af55 100644 --- a/tests/ui/needless_late_init.rs +++ b/tests/ui/needless_late_init.rs @@ -1,5 +1,6 @@ +// run-rustfix #![feature(let_chains)] -#![allow(unused, clippy::nonminimal_bool, clippy::let_unit_value)] +#![allow(unused, clippy::nonminimal_bool, clippy::let_unit_value, clippy::let_and_return)] use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::rc::Rc; diff --git a/tests/ui/needless_late_init.stderr b/tests/ui/needless_late_init.stderr index d33a117b288cb..fb9858f4a7c33 100644 --- a/tests/ui/needless_late_init.stderr +++ b/tests/ui/needless_late_init.stderr @@ -1,5 +1,5 @@ error: unneeded late initialization - --> $DIR/needless_late_init.rs:15:5 + --> $DIR/needless_late_init.rs:16:5 | LL | let a; | ^^^^^^ @@ -21,7 +21,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:24:5 + --> $DIR/needless_late_init.rs:25:5 | LL | let b; | ^^^^^^ @@ -42,7 +42,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:31:5 + --> $DIR/needless_late_init.rs:32:5 | LL | let d; | ^^^^^^ @@ -63,7 +63,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:39:5 + --> $DIR/needless_late_init.rs:40:5 | LL | let e; | ^^^^^^ @@ -84,7 +84,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:46:5 + --> $DIR/needless_late_init.rs:47:5 | LL | let f; | ^^^^^^ @@ -100,7 +100,7 @@ LL + 1 => "three", | error: unneeded late initialization - --> $DIR/needless_late_init.rs:52:5 + --> $DIR/needless_late_init.rs:53:5 | LL | let g: usize; | ^^^^^^^^^^^^^ @@ -120,7 +120,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:60:5 + --> $DIR/needless_late_init.rs:61:5 | LL | let x; | ^^^^^^ created here @@ -134,7 +134,7 @@ LL | let x = 1; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:64:5 + --> $DIR/needless_late_init.rs:65:5 | LL | let x; | ^^^^^^ created here @@ -148,7 +148,7 @@ LL | let x = SignificantDrop; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:68:5 + --> $DIR/needless_late_init.rs:69:5 | LL | let x; | ^^^^^^ created here @@ -162,7 +162,7 @@ LL | let x = SignificantDrop; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:87:5 + --> $DIR/needless_late_init.rs:88:5 | LL | let a; | ^^^^^^ @@ -183,7 +183,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:104:5 + --> $DIR/needless_late_init.rs:105:5 | LL | let a; | ^^^^^^ diff --git a/tests/ui/nonminimal_bool_methods.fixed b/tests/ui/nonminimal_bool_methods.fixed new file mode 100644 index 0000000000000..aad44089de499 --- /dev/null +++ b/tests/ui/nonminimal_bool_methods.fixed @@ -0,0 +1,111 @@ +// run-rustfix +#![allow(unused, clippy::diverging_sub_expression)] +#![warn(clippy::nonminimal_bool)] + +fn methods_with_negation() { + let a: Option = unimplemented!(); + let b: Result = unimplemented!(); + let _ = a.is_some(); + let _ = a.is_none(); + let _ = a.is_none(); + let _ = a.is_some(); + let _ = b.is_err(); + let _ = b.is_ok(); + let _ = b.is_ok(); + let _ = b.is_err(); + let c = false; + let _ = a.is_none() || c; + let _ = a.is_none() && c; + let _ = !(!c ^ c) || a.is_none(); + let _ = (!c ^ c) || a.is_none(); + let _ = !c ^ c || a.is_none(); +} + +// Simplified versions of https://github.com/rust-lang/rust-clippy/issues/2638 +// clippy::nonminimal_bool should only check the built-in Result and Some type, not +// any other types like the following. +enum CustomResultOk { + Ok, + Err(E), +} +enum CustomResultErr { + Ok, + Err(E), +} +enum CustomSomeSome { + Some(T), + None, +} +enum CustomSomeNone { + Some(T), + None, +} + +impl CustomResultOk { + pub fn is_ok(&self) -> bool { + true + } +} + +impl CustomResultErr { + pub fn is_err(&self) -> bool { + true + } +} + +impl CustomSomeSome { + pub fn is_some(&self) -> bool { + true + } +} + +impl CustomSomeNone { + pub fn is_none(&self) -> bool { + true + } +} + +fn dont_warn_for_custom_methods_with_negation() { + let res = CustomResultOk::Err("Error"); + // Should not warn and suggest 'is_err()' because the type does not + // implement is_err(). + if !res.is_ok() {} + + let res = CustomResultErr::Err("Error"); + // Should not warn and suggest 'is_ok()' because the type does not + // implement is_ok(). + if !res.is_err() {} + + let res = CustomSomeSome::Some("thing"); + // Should not warn and suggest 'is_none()' because the type does not + // implement is_none(). + if !res.is_some() {} + + let res = CustomSomeNone::Some("thing"); + // Should not warn and suggest 'is_some()' because the type does not + // implement is_some(). + if !res.is_none() {} +} + +// Only Built-in Result and Some types should suggest the negated alternative +fn warn_for_built_in_methods_with_negation() { + let res: Result = Ok(1); + if res.is_err() {} + if res.is_ok() {} + + let res = Some(1); + if res.is_none() {} + if res.is_some() {} +} + +#[allow(clippy::neg_cmp_op_on_partial_ord)] +fn dont_warn_for_negated_partial_ord_comparison() { + let a: f64 = unimplemented!(); + let b: f64 = unimplemented!(); + let _ = !(a < b); + let _ = !(a <= b); + let _ = !(a > b); + let _ = !(a >= b); +} + +fn main() {} diff --git a/tests/ui/nonminimal_bool_methods.rs b/tests/ui/nonminimal_bool_methods.rs index d0a289b7ea43e..b9074da842706 100644 --- a/tests/ui/nonminimal_bool_methods.rs +++ b/tests/ui/nonminimal_bool_methods.rs @@ -1,3 +1,4 @@ +// run-rustfix #![allow(unused, clippy::diverging_sub_expression)] #![warn(clippy::nonminimal_bool)] diff --git a/tests/ui/nonminimal_bool_methods.stderr b/tests/ui/nonminimal_bool_methods.stderr index a2df889d62302..21b84db858909 100644 --- a/tests/ui/nonminimal_bool_methods.stderr +++ b/tests/ui/nonminimal_bool_methods.stderr @@ -1,5 +1,5 @@ error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:8:13 + --> $DIR/nonminimal_bool_methods.rs:9:13 | LL | let _ = !a.is_some(); | ^^^^^^^^^^^^ help: try: `a.is_none()` @@ -7,73 +7,73 @@ LL | let _ = !a.is_some(); = note: `-D clippy::nonminimal-bool` implied by `-D warnings` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:10:13 + --> $DIR/nonminimal_bool_methods.rs:11:13 | LL | let _ = !a.is_none(); | ^^^^^^^^^^^^ help: try: `a.is_some()` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:12:13 + --> $DIR/nonminimal_bool_methods.rs:13:13 | LL | let _ = !b.is_err(); | ^^^^^^^^^^^ help: try: `b.is_ok()` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:14:13 + --> $DIR/nonminimal_bool_methods.rs:15:13 | LL | let _ = !b.is_ok(); | ^^^^^^^^^^ help: try: `b.is_err()` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:16:13 + --> $DIR/nonminimal_bool_methods.rs:17:13 | LL | let _ = !(a.is_some() && !c); | ^^^^^^^^^^^^^^^^^^^^ help: try: `a.is_none() || c` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:17:13 + --> $DIR/nonminimal_bool_methods.rs:18:13 | LL | let _ = !(a.is_some() || !c); | ^^^^^^^^^^^^^^^^^^^^ help: try: `a.is_none() && c` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:18:26 + --> $DIR/nonminimal_bool_methods.rs:19:26 | LL | let _ = !(!c ^ c) || !a.is_some(); | ^^^^^^^^^^^^ help: try: `a.is_none()` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:19:25 + --> $DIR/nonminimal_bool_methods.rs:20:25 | LL | let _ = (!c ^ c) || !a.is_some(); | ^^^^^^^^^^^^ help: try: `a.is_none()` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:20:23 + --> $DIR/nonminimal_bool_methods.rs:21:23 | LL | let _ = !c ^ c || !a.is_some(); | ^^^^^^^^^^^^ help: try: `a.is_none()` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:92:8 + --> $DIR/nonminimal_bool_methods.rs:93:8 | LL | if !res.is_ok() {} | ^^^^^^^^^^^^ help: try: `res.is_err()` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:93:8 + --> $DIR/nonminimal_bool_methods.rs:94:8 | LL | if !res.is_err() {} | ^^^^^^^^^^^^^ help: try: `res.is_ok()` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:96:8 + --> $DIR/nonminimal_bool_methods.rs:97:8 | LL | if !res.is_some() {} | ^^^^^^^^^^^^^^ help: try: `res.is_none()` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:97:8 + --> $DIR/nonminimal_bool_methods.rs:98:8 | LL | if !res.is_none() {} | ^^^^^^^^^^^^^^ help: try: `res.is_some()` diff --git a/tests/ui/rc_buffer.fixed b/tests/ui/rc_buffer.fixed new file mode 100644 index 0000000000000..8910c01b1fcf9 --- /dev/null +++ b/tests/ui/rc_buffer.fixed @@ -0,0 +1,28 @@ +// run-rustfix +#![warn(clippy::rc_buffer)] +#![allow(dead_code, unused_imports)] + +use std::cell::RefCell; +use std::ffi::OsString; +use std::path::PathBuf; +use std::rc::Rc; + +struct S { + // triggers lint + bad1: Rc, + bad2: Rc, + bad3: Rc<[u8]>, + bad4: Rc, + // does not trigger lint + good1: Rc>, +} + +// triggers lint +fn func_bad1(_: Rc) {} +fn func_bad2(_: Rc) {} +fn func_bad3(_: Rc<[u8]>) {} +fn func_bad4(_: Rc) {} +// does not trigger lint +fn func_good1(_: Rc>) {} + +fn main() {} diff --git a/tests/ui/rc_buffer.rs b/tests/ui/rc_buffer.rs index 1fa9864393687..1e63a43262ec1 100644 --- a/tests/ui/rc_buffer.rs +++ b/tests/ui/rc_buffer.rs @@ -1,4 +1,6 @@ +// run-rustfix #![warn(clippy::rc_buffer)] +#![allow(dead_code, unused_imports)] use std::cell::RefCell; use std::ffi::OsString; diff --git a/tests/ui/rc_buffer.stderr b/tests/ui/rc_buffer.stderr index e4cc169af07b9..9ed028e3df41b 100644 --- a/tests/ui/rc_buffer.stderr +++ b/tests/ui/rc_buffer.stderr @@ -1,5 +1,5 @@ error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:10:11 + --> $DIR/rc_buffer.rs:12:11 | LL | bad1: Rc, | ^^^^^^^^^^ help: try: `Rc` @@ -7,43 +7,43 @@ LL | bad1: Rc, = note: `-D clippy::rc-buffer` implied by `-D warnings` error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:11:11 + --> $DIR/rc_buffer.rs:13:11 | LL | bad2: Rc, | ^^^^^^^^^^^ help: try: `Rc` error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:12:11 + --> $DIR/rc_buffer.rs:14:11 | LL | bad3: Rc>, | ^^^^^^^^^^^ help: try: `Rc<[u8]>` error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:13:11 + --> $DIR/rc_buffer.rs:15:11 | LL | bad4: Rc, | ^^^^^^^^^^^^ help: try: `Rc` error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:19:17 + --> $DIR/rc_buffer.rs:21:17 | LL | fn func_bad1(_: Rc) {} | ^^^^^^^^^^ help: try: `Rc` error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:20:17 + --> $DIR/rc_buffer.rs:22:17 | LL | fn func_bad2(_: Rc) {} | ^^^^^^^^^^^ help: try: `Rc` error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:21:17 + --> $DIR/rc_buffer.rs:23:17 | LL | fn func_bad3(_: Rc>) {} | ^^^^^^^^^^^ help: try: `Rc<[u8]>` error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:22:17 + --> $DIR/rc_buffer.rs:24:17 | LL | fn func_bad4(_: Rc) {} | ^^^^^^^^^^^^ help: try: `Rc` diff --git a/tests/ui/rc_buffer_arc.fixed b/tests/ui/rc_buffer_arc.fixed new file mode 100644 index 0000000000000..13dd6f5fcd186 --- /dev/null +++ b/tests/ui/rc_buffer_arc.fixed @@ -0,0 +1,27 @@ +// run-rustfix +#![warn(clippy::rc_buffer)] +#![allow(dead_code, unused_imports)] + +use std::ffi::OsString; +use std::path::PathBuf; +use std::sync::{Arc, Mutex}; + +struct S { + // triggers lint + bad1: Arc, + bad2: Arc, + bad3: Arc<[u8]>, + bad4: Arc, + // does not trigger lint + good1: Arc>, +} + +// triggers lint +fn func_bad1(_: Arc) {} +fn func_bad2(_: Arc) {} +fn func_bad3(_: Arc<[u8]>) {} +fn func_bad4(_: Arc) {} +// does not trigger lint +fn func_good1(_: Arc>) {} + +fn main() {} diff --git a/tests/ui/rc_buffer_arc.rs b/tests/ui/rc_buffer_arc.rs index 5d586584817bd..1a521bfeb7c86 100644 --- a/tests/ui/rc_buffer_arc.rs +++ b/tests/ui/rc_buffer_arc.rs @@ -1,4 +1,6 @@ +// run-rustfix #![warn(clippy::rc_buffer)] +#![allow(dead_code, unused_imports)] use std::ffi::OsString; use std::path::PathBuf; diff --git a/tests/ui/rc_buffer_arc.stderr b/tests/ui/rc_buffer_arc.stderr index 8252270d2ac74..911feea73529d 100644 --- a/tests/ui/rc_buffer_arc.stderr +++ b/tests/ui/rc_buffer_arc.stderr @@ -1,5 +1,5 @@ error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:9:11 + --> $DIR/rc_buffer_arc.rs:11:11 | LL | bad1: Arc, | ^^^^^^^^^^^ help: try: `Arc` @@ -7,43 +7,43 @@ LL | bad1: Arc, = note: `-D clippy::rc-buffer` implied by `-D warnings` error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:10:11 + --> $DIR/rc_buffer_arc.rs:12:11 | LL | bad2: Arc, | ^^^^^^^^^^^^ help: try: `Arc` error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:11:11 + --> $DIR/rc_buffer_arc.rs:13:11 | LL | bad3: Arc>, | ^^^^^^^^^^^^ help: try: `Arc<[u8]>` error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:12:11 + --> $DIR/rc_buffer_arc.rs:14:11 | LL | bad4: Arc, | ^^^^^^^^^^^^^ help: try: `Arc` error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:18:17 + --> $DIR/rc_buffer_arc.rs:20:17 | LL | fn func_bad1(_: Arc) {} | ^^^^^^^^^^^ help: try: `Arc` error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:19:17 + --> $DIR/rc_buffer_arc.rs:21:17 | LL | fn func_bad2(_: Arc) {} | ^^^^^^^^^^^^ help: try: `Arc` error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:20:17 + --> $DIR/rc_buffer_arc.rs:22:17 | LL | fn func_bad3(_: Arc>) {} | ^^^^^^^^^^^^ help: try: `Arc<[u8]>` error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:21:17 + --> $DIR/rc_buffer_arc.rs:23:17 | LL | fn func_bad4(_: Arc) {} | ^^^^^^^^^^^^^ help: try: `Arc` diff --git a/tests/ui/suspicious_operation_groupings.fixed b/tests/ui/suspicious_operation_groupings.fixed new file mode 100644 index 0000000000000..ede8a39fed718 --- /dev/null +++ b/tests/ui/suspicious_operation_groupings.fixed @@ -0,0 +1,209 @@ +// run-rustfix +#![warn(clippy::suspicious_operation_groupings)] +#![allow(dead_code, unused_parens, clippy::eq_op)] + +struct Vec3 { + x: f64, + y: f64, + z: f64, +} + +impl Eq for Vec3 {} + +impl PartialEq for Vec3 { + fn eq(&self, other: &Self) -> bool { + // This should trigger the lint because `self.x` is compared to `other.y` + self.x == other.x && self.y == other.y && self.z == other.z + } +} + +struct S { + a: i32, + b: i32, + c: i32, + d: i32, +} + +fn buggy_ab_cmp(s1: &S, s2: &S) -> bool { + // There's no `s1.b` + s1.a < s2.a && s1.b < s2.b +} + +struct SaOnly { + a: i32, +} + +impl S { + fn a(&self) -> i32 { + 0 + } +} + +fn do_not_give_bad_suggestions_for_this_unusual_expr(s1: &S, s2: &SaOnly) -> bool { + // This is superficially similar to `buggy_ab_cmp`, but we should not suggest + // `s2.b` since that is invalid. + s1.a < s2.a && s1.a() < s1.b +} + +fn do_not_give_bad_suggestions_for_this_macro_expr(s1: &S, s2: &SaOnly) -> bool { + macro_rules! s1 { + () => { + S { + a: 1, + b: 1, + c: 1, + d: 1, + } + }; + } + + // This is superficially similar to `buggy_ab_cmp`, but we should not suggest + // `s2.b` since that is invalid. + s1.a < s2.a && s1!().a < s1.b +} + +fn do_not_give_bad_suggestions_for_this_incorrect_expr(s1: &S, s2: &SaOnly) -> bool { + // There's two `s1.b`, but we should not suggest `s2.b` since that is invalid + s1.a < s2.a && s1.b < s1.b +} + +fn permissable(s1: &S, s2: &S) -> bool { + // Something like this seems like it might actually be what is desired. + s1.a == s2.b +} + +fn non_boolean_operators(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + s1.a * s2.a + s1.b * s2.b + s1.c * s2.c + s1.d * s2.d +} + +fn odd_number_of_pairs(s1: &S, s2: &S) -> i32 { + // There's no `s2.b` + s1.a * s2.a + s1.b * s2.b + s1.c * s2.c +} + +fn not_caught_by_eq_op_middle_change_left(s1: &S, s2: &S) -> i32 { + // There's no `s1.b` + s1.a * s2.a + s1.b * s2.b + s1.c * s2.c +} + +fn not_caught_by_eq_op_middle_change_right(s1: &S, s2: &S) -> i32 { + // There's no `s2.b` + s1.a * s2.a + s1.b * s2.b + s1.c * s2.c +} + +fn not_caught_by_eq_op_start(s1: &S, s2: &S) -> i32 { + // There's no `s2.a` + s1.a * s2.a + s1.b * s2.b + s1.c * s2.c +} + +fn not_caught_by_eq_op_end(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + s1.a * s2.a + s1.b * s2.b + s1.c * s2.c +} + +fn the_cross_product_should_not_lint(s1: &S, s2: &S) -> (i32, i32, i32) { + ( + s1.b * s2.c - s1.c * s2.b, + s1.c * s2.a - s1.a * s2.c, + s1.a * s2.b - s1.b * s2.a, + ) +} + +fn outer_parens_simple(s1: &S, s2: &S) -> i32 { + // There's no `s2.b` + (s1.a * s2.a + s1.b * s2.b) +} + +fn outer_parens(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + (s1.a * s2.a + s1.b * s2.b + s1.c * s2.c + s1.d * s2.d) +} + +fn inner_parens(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + (s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.c) + (s1.d * s2.d) +} + +fn outer_and_some_inner_parens(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + ((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.c) + (s1.d * s2.d)) +} + +fn all_parens_balanced_tree(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.c) + (s1.d * s2.d))) +} + +fn all_parens_left_tree(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + (((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.c)) + (s1.d * s2.d)) +} + +fn all_parens_right_tree(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + ((s1.a * s2.a) + ((s1.b * s2.b) + (s1.c * s2.c) + (s1.d * s2.d))) +} + +fn inside_other_binop_expression(s1: &S, s2: &S) -> i32 { + // There's no `s1.b` + (s1.a * s2.a + s1.b * s2.b) / 2 +} + +fn inside_function_call(s1: &S, s2: &S) -> i32 { + // There's no `s1.b` + i32::swap_bytes(s1.a * s2.a + s1.b * s2.b) +} + +fn inside_larger_boolean_expression(s1: &S, s2: &S) -> bool { + // There's no `s1.c` + s1.a > 0 && s1.b > 0 && s1.c == s2.c && s1.d == s2.d +} + +fn inside_larger_boolean_expression_with_unsorted_ops(s1: &S, s2: &S) -> bool { + // There's no `s1.c` + s1.a > 0 && s1.c == s2.c && s1.b > 0 && s1.d == s2.d +} + +struct Nested { + inner: ((i32,), (i32,), (i32,)), +} + +fn changed_middle_ident(n1: &Nested, n2: &Nested) -> bool { + // There's no `n2.inner.2.0` + (n1.inner.0).0 == (n2.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0 +} + +// `eq_op` should catch this one. +fn changed_initial_ident(n1: &Nested, n2: &Nested) -> bool { + // There's no `n2.inner.0.0` + (n1.inner.0).0 == (n1.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0 +} + +fn inside_fn_with_similar_expression(s1: &S, s2: &S, strict: bool) -> bool { + if strict { + s1.a < s2.a && s1.b < s2.b + } else { + // There's no `s1.b` in this subexpression + s1.a <= s2.a && s1.b <= s2.b + } +} + +fn inside_an_if_statement(s1: &mut S, s2: &S) { + // There's no `s1.b` + if s1.a < s2.a && s1.b < s2.b { + s1.c = s2.c; + } +} + +fn maximum_unary_minus_right_tree(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + -(-(-s1.a * -s2.a) + (-(-s1.b * -s2.b) + -(-s1.c * -s2.c) + -(-s1.d * -s2.d))) +} + +fn unary_minus_and_an_if_expression(s1: &S, s2: &S) -> i32 { + // There's no `s1.b` + -(if -s1.a < -s2.a && -s1.b < -s2.b { s1.c } else { s2.a }) +} + +fn main() {} diff --git a/tests/ui/suspicious_operation_groupings.rs b/tests/ui/suspicious_operation_groupings.rs index 3201d5de0f356..26ce97bb37f84 100644 --- a/tests/ui/suspicious_operation_groupings.rs +++ b/tests/ui/suspicious_operation_groupings.rs @@ -1,5 +1,6 @@ +// run-rustfix #![warn(clippy::suspicious_operation_groupings)] -#![allow(clippy::eq_op)] +#![allow(dead_code, unused_parens, clippy::eq_op)] struct Vec3 { x: f64, diff --git a/tests/ui/suspicious_operation_groupings.stderr b/tests/ui/suspicious_operation_groupings.stderr index baf9bc74b000e..29f229245fed3 100644 --- a/tests/ui/suspicious_operation_groupings.stderr +++ b/tests/ui/suspicious_operation_groupings.stderr @@ -1,5 +1,5 @@ error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:15:9 + --> $DIR/suspicious_operation_groupings.rs:16:9 | LL | self.x == other.y && self.y == other.y && self.z == other.z | ^^^^^^^^^^^^^^^^^ help: did you mean: `self.x == other.x` @@ -7,151 +7,151 @@ LL | self.x == other.y && self.y == other.y && self.z == other.z = note: `-D clippy::suspicious-operation-groupings` implied by `-D warnings` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:28:20 + --> $DIR/suspicious_operation_groupings.rs:29:20 | LL | s1.a < s2.a && s1.a < s2.b | ^^^^^^^^^^^ help: did you mean: `s1.b < s2.b` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:76:33 + --> $DIR/suspicious_operation_groupings.rs:77:33 | LL | s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:81:19 + --> $DIR/suspicious_operation_groupings.rs:82:19 | LL | s1.a * s2.a + s1.b * s2.c + s1.c * s2.c | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:81:19 + --> $DIR/suspicious_operation_groupings.rs:82:19 | LL | s1.a * s2.a + s1.b * s2.c + s1.c * s2.c | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:86:19 + --> $DIR/suspicious_operation_groupings.rs:87:19 | LL | s1.a * s2.a + s2.b * s2.b + s1.c * s2.c | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:91:19 + --> $DIR/suspicious_operation_groupings.rs:92:19 | LL | s1.a * s2.a + s1.b * s1.b + s1.c * s2.c | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:96:5 + --> $DIR/suspicious_operation_groupings.rs:97:5 | LL | s1.a * s1.a + s1.b * s2.b + s1.c * s2.c | ^^^^^^^^^^^ help: did you mean: `s1.a * s2.a` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:101:33 + --> $DIR/suspicious_operation_groupings.rs:102:33 | LL | s1.a * s2.a + s1.b * s2.b + s1.c * s1.c | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:114:20 + --> $DIR/suspicious_operation_groupings.rs:115:20 | LL | (s1.a * s2.a + s1.b * s1.b) | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:119:34 + --> $DIR/suspicious_operation_groupings.rs:120:34 | LL | (s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d) | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:124:38 + --> $DIR/suspicious_operation_groupings.rs:125:38 | LL | (s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d) | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:129:39 + --> $DIR/suspicious_operation_groupings.rs:130:39 | LL | ((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d)) | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:134:42 + --> $DIR/suspicious_operation_groupings.rs:135:42 | LL | (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.b) + (s1.d * s2.d))) | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:134:42 + --> $DIR/suspicious_operation_groupings.rs:135:42 | LL | (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.b) + (s1.d * s2.d))) | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:139:40 + --> $DIR/suspicious_operation_groupings.rs:140:40 | LL | (((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b)) + (s1.d * s2.d)) | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:144:40 + --> $DIR/suspicious_operation_groupings.rs:145:40 | LL | ((s1.a * s2.a) + ((s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d))) | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:149:20 + --> $DIR/suspicious_operation_groupings.rs:150:20 | LL | (s1.a * s2.a + s2.b * s2.b) / 2 | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:154:35 + --> $DIR/suspicious_operation_groupings.rs:155:35 | LL | i32::swap_bytes(s1.a * s2.a + s2.b * s2.b) | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:159:29 + --> $DIR/suspicious_operation_groupings.rs:160:29 | LL | s1.a > 0 && s1.b > 0 && s1.d == s2.c && s1.d == s2.d | ^^^^^^^^^^^^ help: did you mean: `s1.c == s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:164:17 + --> $DIR/suspicious_operation_groupings.rs:165:17 | LL | s1.a > 0 && s1.d == s2.c && s1.b > 0 && s1.d == s2.d | ^^^^^^^^^^^^ help: did you mean: `s1.c == s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:173:77 + --> $DIR/suspicious_operation_groupings.rs:174:77 | LL | (n1.inner.0).0 == (n2.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.1).0 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `(n1.inner.2).0 == (n2.inner.2).0` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:187:25 + --> $DIR/suspicious_operation_groupings.rs:188:25 | LL | s1.a <= s2.a && s1.a <= s2.b | ^^^^^^^^^^^^ help: did you mean: `s1.b <= s2.b` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:193:23 + --> $DIR/suspicious_operation_groupings.rs:194:23 | LL | if s1.a < s2.a && s1.a < s2.b { | ^^^^^^^^^^^ help: did you mean: `s1.b < s2.b` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:200:48 + --> $DIR/suspicious_operation_groupings.rs:201:48 | LL | -(-(-s1.a * -s2.a) + (-(-s1.b * -s2.b) + -(-s1.c * -s2.b) + -(-s1.d * -s2.d))) | ^^^^^^^^^^^^^ help: did you mean: `-s1.c * -s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:205:27 + --> $DIR/suspicious_operation_groupings.rs:206:27 | LL | -(if -s1.a < -s2.a && -s1.a < -s2.b { s1.c } else { s2.a }) | ^^^^^^^^^^^^^ help: did you mean: `-s1.b < -s2.b` diff --git a/tests/ui/unicode.fixed b/tests/ui/unicode.fixed new file mode 100644 index 0000000000000..328cda369e11b --- /dev/null +++ b/tests/ui/unicode.fixed @@ -0,0 +1,36 @@ +// run-rustfix +#[warn(clippy::invisible_characters)] +fn zero() { + print!("Here >\u{200B}< is a ZWS, and \u{200B}another"); + print!("This\u{200B}is\u{200B}fine"); + print!("Here >\u{AD}< is a SHY, and \u{AD}another"); + print!("This\u{ad}is\u{ad}fine"); + print!("Here >\u{2060}< is a WJ, and \u{2060}another"); + print!("This\u{2060}is\u{2060}fine"); +} + +#[warn(clippy::unicode_not_nfc)] +fn canon() { + print!("̀àh?"); + print!("a\u{0300}h?"); // also ok +} + +#[warn(clippy::non_ascii_literal)] +fn uni() { + print!("\u{dc}ben!"); + print!("\u{DC}ben!"); // this is ok +} + +// issue 8013 +#[warn(clippy::non_ascii_literal)] +fn single_quote() { + const _EMPTY_BLOCK: char = '\u{25b1}'; + const _FULL_BLOCK: char = '\u{25b0}'; +} + +fn main() { + zero(); + uni(); + canon(); + single_quote(); +} diff --git a/tests/ui/unicode.rs b/tests/ui/unicode.rs index e0a4eadce33bd..7828d6bcbea7a 100644 --- a/tests/ui/unicode.rs +++ b/tests/ui/unicode.rs @@ -1,3 +1,4 @@ +// run-rustfix #[warn(clippy::invisible_characters)] fn zero() { print!("Here >​< is a ZWS, and ​another"); diff --git a/tests/ui/unicode.stderr b/tests/ui/unicode.stderr index 3f54e3880e747..01d3f3c029679 100644 --- a/tests/ui/unicode.stderr +++ b/tests/ui/unicode.stderr @@ -1,5 +1,5 @@ error: invisible character detected - --> $DIR/unicode.rs:3:12 + --> $DIR/unicode.rs:4:12 | LL | print!("Here >​< is a ZWS, and ​another"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{200B}< is a ZWS, and /u{200B}another"` @@ -7,19 +7,19 @@ LL | print!("Here >​< is a ZWS, and ​another"); = note: `-D clippy::invisible-characters` implied by `-D warnings` error: invisible character detected - --> $DIR/unicode.rs:5:12 + --> $DIR/unicode.rs:6:12 | LL | print!("Here >­< is a SHY, and ­another"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{AD}< is a SHY, and /u{AD}another"` error: invisible character detected - --> $DIR/unicode.rs:7:12 + --> $DIR/unicode.rs:8:12 | LL | print!("Here >⁠< is a WJ, and ⁠another"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{2060}< is a WJ, and /u{2060}another"` error: non-NFC Unicode sequence detected - --> $DIR/unicode.rs:13:12 + --> $DIR/unicode.rs:14:12 | LL | print!("̀àh?"); | ^^^^^ help: consider replacing the string with: `"̀àh?"` @@ -27,7 +27,7 @@ LL | print!("̀àh?"); = note: `-D clippy::unicode-not-nfc` implied by `-D warnings` error: literal non-ASCII character detected - --> $DIR/unicode.rs:19:12 + --> $DIR/unicode.rs:20:12 | LL | print!("Üben!"); | ^^^^^^^ help: consider replacing the string with: `"/u{dc}ben!"` @@ -35,13 +35,13 @@ LL | print!("Üben!"); = note: `-D clippy::non-ascii-literal` implied by `-D warnings` error: literal non-ASCII character detected - --> $DIR/unicode.rs:26:32 + --> $DIR/unicode.rs:27:32 | LL | const _EMPTY_BLOCK: char = '▱'; | ^^^ help: consider replacing the string with: `'/u{25b1}'` error: literal non-ASCII character detected - --> $DIR/unicode.rs:27:31 + --> $DIR/unicode.rs:28:31 | LL | const _FULL_BLOCK: char = '▰'; | ^^^ help: consider replacing the string with: `'/u{25b0}'` diff --git a/tests/ui/unit_arg_empty_blocks.fixed b/tests/ui/unit_arg_empty_blocks.fixed new file mode 100644 index 0000000000000..9400e93cac839 --- /dev/null +++ b/tests/ui/unit_arg_empty_blocks.fixed @@ -0,0 +1,30 @@ +// run-rustfix +#![warn(clippy::unit_arg)] +#![allow(clippy::no_effect, unused_must_use, unused_variables)] + +use std::fmt::Debug; + +fn foo(t: T) { + println!("{:?}", t); +} + +fn foo3(t1: T1, t2: T2, t3: T3) { + println!("{:?}, {:?}, {:?}", t1, t2, t3); +} + +fn bad() { + foo(()); + foo3((), 2, 2); + foo(0); + taking_two_units((), ()); + foo(0); + foo(1); + taking_three_units((), (), ()); +} + +fn taking_two_units(a: (), b: ()) {} +fn taking_three_units(a: (), b: (), c: ()) {} + +fn main() { + bad(); +} diff --git a/tests/ui/unit_arg_empty_blocks.rs b/tests/ui/unit_arg_empty_blocks.rs index 18a31eb3deee2..5f52b6c5315fd 100644 --- a/tests/ui/unit_arg_empty_blocks.rs +++ b/tests/ui/unit_arg_empty_blocks.rs @@ -1,3 +1,4 @@ +// run-rustfix #![warn(clippy::unit_arg)] #![allow(clippy::no_effect, unused_must_use, unused_variables)] diff --git a/tests/ui/unit_arg_empty_blocks.stderr b/tests/ui/unit_arg_empty_blocks.stderr index 39072c9a8cc0f..d35e931697d21 100644 --- a/tests/ui/unit_arg_empty_blocks.stderr +++ b/tests/ui/unit_arg_empty_blocks.stderr @@ -1,5 +1,5 @@ error: passing a unit value to a function - --> $DIR/unit_arg_empty_blocks.rs:15:5 + --> $DIR/unit_arg_empty_blocks.rs:16:5 | LL | foo({}); | ^^^^--^ @@ -9,7 +9,7 @@ LL | foo({}); = note: `-D clippy::unit-arg` implied by `-D warnings` error: passing a unit value to a function - --> $DIR/unit_arg_empty_blocks.rs:16:5 + --> $DIR/unit_arg_empty_blocks.rs:17:5 | LL | foo3({}, 2, 2); | ^^^^^--^^^^^^^ @@ -17,7 +17,7 @@ LL | foo3({}, 2, 2); | help: use a unit literal instead: `()` error: passing unit values to a function - --> $DIR/unit_arg_empty_blocks.rs:17:5 + --> $DIR/unit_arg_empty_blocks.rs:18:5 | LL | taking_two_units({}, foo(0)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -29,7 +29,7 @@ LL ~ taking_two_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg_empty_blocks.rs:18:5 + --> $DIR/unit_arg_empty_blocks.rs:19:5 | LL | taking_three_units({}, foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/unnecessary_cast.fixed b/tests/ui/unnecessary_cast.fixed new file mode 100644 index 0000000000000..e327a38a76d90 --- /dev/null +++ b/tests/ui/unnecessary_cast.fixed @@ -0,0 +1,40 @@ +// run-rustfix +#![warn(clippy::unnecessary_cast)] +#![allow(unused_must_use, clippy::no_effect)] + +#[rustfmt::skip] +fn main() { + // Test cast_unnecessary + 1_i32; + 1_f32; + false; + &1i32 as &i32; + + -1_i32; + - 1_i32; + -1_f32; + 1_i32; + 1_f32; + + // macro version + macro_rules! foo { + ($a:ident, $b:ident) => { + #[allow(unused)] + pub fn $a() -> $b { + 1 as $b + } + }; + } + foo!(a, i32); + foo!(b, f32); + foo!(c, f64); + + // do not lint cast to cfg-dependant type + 1 as std::os::raw::c_char; + + // do not lint cast to alias type + 1 as I32Alias; + &1 as &I32Alias; +} + +type I32Alias = i32; diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index 62c3e9636866a..78affa453c0eb 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -1,5 +1,6 @@ +// run-rustfix #![warn(clippy::unnecessary_cast)] -#![allow(clippy::no_effect)] +#![allow(unused_must_use, clippy::no_effect)] #[rustfmt::skip] fn main() { diff --git a/tests/ui/unnecessary_cast.stderr b/tests/ui/unnecessary_cast.stderr index a5a93c6110c6a..d1b81ce093778 100644 --- a/tests/ui/unnecessary_cast.stderr +++ b/tests/ui/unnecessary_cast.stderr @@ -1,5 +1,5 @@ error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:7:5 + --> $DIR/unnecessary_cast.rs:8:5 | LL | 1i32 as i32; | ^^^^^^^^^^^ help: try: `1_i32` @@ -7,43 +7,43 @@ LL | 1i32 as i32; = note: `-D clippy::unnecessary-cast` implied by `-D warnings` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:8:5 + --> $DIR/unnecessary_cast.rs:9:5 | LL | 1f32 as f32; | ^^^^^^^^^^^ help: try: `1_f32` error: casting to the same type is unnecessary (`bool` -> `bool`) - --> $DIR/unnecessary_cast.rs:9:5 + --> $DIR/unnecessary_cast.rs:10:5 | LL | false as bool; | ^^^^^^^^^^^^^ help: try: `false` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:12:5 + --> $DIR/unnecessary_cast.rs:13:5 | LL | -1_i32 as i32; | ^^^^^^^^^^^^^ help: try: `-1_i32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:13:5 + --> $DIR/unnecessary_cast.rs:14:5 | LL | - 1_i32 as i32; | ^^^^^^^^^^^^^^ help: try: `- 1_i32` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:14:5 + --> $DIR/unnecessary_cast.rs:15:5 | LL | -1f32 as f32; | ^^^^^^^^^^^^ help: try: `-1_f32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:15:5 + --> $DIR/unnecessary_cast.rs:16:5 | LL | 1_i32 as i32; | ^^^^^^^^^^^^ help: try: `1_i32` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:16:5 + --> $DIR/unnecessary_cast.rs:17:5 | LL | 1_f32 as f32; | ^^^^^^^^^^^^ help: try: `1_f32` From c91a7f0b83853a4cfb9235cd75c593eecef5e1ff Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Tue, 24 May 2022 19:01:18 -0400 Subject: [PATCH 40/79] Address review comments --- tests/compile-test.rs | 13 ++- tests/ui/needless_late_init.fixed | 28 ++++- tests/ui/needless_late_init.rs | 28 ++++- tests/ui/needless_late_init.stderr | 91 +++++++++++++--- tests/ui/needless_late_init_fixable.fixed | 19 ---- tests/ui/needless_late_init_fixable.rs | 19 ---- tests/ui/needless_late_init_fixable.stderr | 70 ------------ tests/ui/unnecessary_cast.fixed | 53 ++++++++- tests/ui/unnecessary_cast.rs | 53 ++++++++- tests/ui/unnecessary_cast.stderr | 120 +++++++++++++++++++-- tests/ui/unnecessary_cast_fixable.fixed | 50 --------- tests/ui/unnecessary_cast_fixable.rs | 50 --------- tests/ui/unnecessary_cast_fixable.stderr | 106 ------------------ 13 files changed, 357 insertions(+), 343 deletions(-) delete mode 100644 tests/ui/needless_late_init_fixable.fixed delete mode 100644 tests/ui/needless_late_init_fixable.rs delete mode 100644 tests/ui/needless_late_init_fixable.stderr delete mode 100644 tests/ui/unnecessary_cast_fixable.fixed delete mode 100644 tests/ui/unnecessary_cast_fixable.rs delete mode 100644 tests/ui/unnecessary_cast_fixable.stderr diff --git a/tests/compile-test.rs b/tests/compile-test.rs index b0bd39801928c..18a51020e14ed 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -165,6 +165,7 @@ fn base_config(test_dir: &str) -> compiletest::Config { fn run_ui() { let mut config = base_config("ui"); config.rustfix_coverage = true; + // use tests/clippy.toml let _g = VarGuard::set("CARGO_MANIFEST_DIR", fs::canonicalize("tests").unwrap()); let _threads = VarGuard::set( "RUST_TEST_THREADS", @@ -384,13 +385,17 @@ fn check_rustfix_coverage() { let missing_coverage_path = Path::new("target/debug/test/ui/rustfix_missing_coverage.txt"); if let Ok(missing_coverage_contents) = std::fs::read_to_string(missing_coverage_path) { - assert!(RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS.is_sorted()); + assert!(RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS.iter().is_sorted_by_key(Path::new)); for rs_path in missing_coverage_contents.lines() { - let filename = rs_path.strip_prefix("tests/ui/").unwrap(); + let filename = Path::new(rs_path).strip_prefix("tests/ui/").unwrap(); assert!( - RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS.binary_search(&filename).is_ok(), - "`{}` runs `MachineApplicable` diagnostics but is missing a `run-rustfix` annotation", + RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS + .binary_search_by_key(&filename, Path::new) + .is_ok(), + "`{}` runs `MachineApplicable` diagnostics but is missing a `run-rustfix` annotation. \ + Please either add `// run-rustfix` at the top of file or add the file to \ + `RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS`.", rs_path, ); } diff --git a/tests/ui/needless_late_init.fixed b/tests/ui/needless_late_init.fixed index 9666b2c84460a..63ae6e389f3d1 100644 --- a/tests/ui/needless_late_init.fixed +++ b/tests/ui/needless_late_init.fixed @@ -1,6 +1,12 @@ // run-rustfix #![feature(let_chains)] -#![allow(unused, clippy::nonminimal_bool, clippy::let_unit_value, clippy::let_and_return)] +#![allow( + unused, + clippy::assign_op_pattern, + clippy::let_and_return, + clippy::let_unit_value, + clippy::nonminimal_bool +)] use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::rc::Rc; @@ -230,3 +236,23 @@ fn does_not_lint() { let y = SignificantDrop; x = SignificantDrop; } + +mod fixable { + #![allow(dead_code)] + + fn main() { + + let a = "zero"; + + + + let b = 1; + let c = 2; + + + let d: usize = 1; + + + let e = format!("{}", d); + } +} diff --git a/tests/ui/needless_late_init.rs b/tests/ui/needless_late_init.rs index c0d11d306af55..89a85c8e053cd 100644 --- a/tests/ui/needless_late_init.rs +++ b/tests/ui/needless_late_init.rs @@ -1,6 +1,12 @@ // run-rustfix #![feature(let_chains)] -#![allow(unused, clippy::nonminimal_bool, clippy::let_unit_value, clippy::let_and_return)] +#![allow( + unused, + clippy::assign_op_pattern, + clippy::let_and_return, + clippy::let_unit_value, + clippy::nonminimal_bool +)] use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::rc::Rc; @@ -230,3 +236,23 @@ fn does_not_lint() { let y = SignificantDrop; x = SignificantDrop; } + +mod fixable { + #![allow(dead_code)] + + fn main() { + let a; + a = "zero"; + + let b; + let c; + b = 1; + c = 2; + + let d: usize; + d = 1; + + let e; + e = format!("{}", d); + } +} diff --git a/tests/ui/needless_late_init.stderr b/tests/ui/needless_late_init.stderr index fb9858f4a7c33..d17666e03ed20 100644 --- a/tests/ui/needless_late_init.stderr +++ b/tests/ui/needless_late_init.stderr @@ -1,5 +1,5 @@ error: unneeded late initialization - --> $DIR/needless_late_init.rs:16:5 + --> $DIR/needless_late_init.rs:22:5 | LL | let a; | ^^^^^^ @@ -21,7 +21,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:25:5 + --> $DIR/needless_late_init.rs:31:5 | LL | let b; | ^^^^^^ @@ -42,7 +42,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:32:5 + --> $DIR/needless_late_init.rs:38:5 | LL | let d; | ^^^^^^ @@ -63,7 +63,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:40:5 + --> $DIR/needless_late_init.rs:46:5 | LL | let e; | ^^^^^^ @@ -84,7 +84,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:47:5 + --> $DIR/needless_late_init.rs:53:5 | LL | let f; | ^^^^^^ @@ -100,7 +100,7 @@ LL + 1 => "three", | error: unneeded late initialization - --> $DIR/needless_late_init.rs:53:5 + --> $DIR/needless_late_init.rs:59:5 | LL | let g: usize; | ^^^^^^^^^^^^^ @@ -120,7 +120,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:61:5 + --> $DIR/needless_late_init.rs:67:5 | LL | let x; | ^^^^^^ created here @@ -134,7 +134,7 @@ LL | let x = 1; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:65:5 + --> $DIR/needless_late_init.rs:71:5 | LL | let x; | ^^^^^^ created here @@ -148,7 +148,7 @@ LL | let x = SignificantDrop; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:69:5 + --> $DIR/needless_late_init.rs:75:5 | LL | let x; | ^^^^^^ created here @@ -162,7 +162,7 @@ LL | let x = SignificantDrop; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:88:5 + --> $DIR/needless_late_init.rs:94:5 | LL | let a; | ^^^^^^ @@ -183,7 +183,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:105:5 + --> $DIR/needless_late_init.rs:111:5 | LL | let a; | ^^^^^^ @@ -203,5 +203,72 @@ help: add a semicolon after the `match` expression LL | }; | + -error: aborting due to 11 previous errors +error: unneeded late initialization + --> $DIR/needless_late_init.rs:244:9 + | +LL | let a; + | ^^^^^^ created here +LL | a = "zero"; + | ^^^^^^^^^^ initialised here + | +help: declare `a` here + | +LL | let a = "zero"; + | ~~~~~ + +error: unneeded late initialization + --> $DIR/needless_late_init.rs:247:9 + | +LL | let b; + | ^^^^^^ created here +LL | let c; +LL | b = 1; + | ^^^^^ initialised here + | +help: declare `b` here + | +LL | let b = 1; + | ~~~~~ + +error: unneeded late initialization + --> $DIR/needless_late_init.rs:248:9 + | +LL | let c; + | ^^^^^^ created here +LL | b = 1; +LL | c = 2; + | ^^^^^ initialised here + | +help: declare `c` here + | +LL | let c = 2; + | ~~~~~ + +error: unneeded late initialization + --> $DIR/needless_late_init.rs:252:9 + | +LL | let d: usize; + | ^^^^^^^^^^^^^ created here +LL | d = 1; + | ^^^^^ initialised here + | +help: declare `d` here + | +LL | let d: usize = 1; + | ~~~~~~~~~~~~ + +error: unneeded late initialization + --> $DIR/needless_late_init.rs:255:9 + | +LL | let e; + | ^^^^^^ created here +LL | e = format!("{}", d); + | ^^^^^^^^^^^^^^^^^^^^ initialised here + | +help: declare `e` here + | +LL | let e = format!("{}", d); + | ~~~~~ + +error: aborting due to 16 previous errors diff --git a/tests/ui/needless_late_init_fixable.fixed b/tests/ui/needless_late_init_fixable.fixed deleted file mode 100644 index 724477e8691df..0000000000000 --- a/tests/ui/needless_late_init_fixable.fixed +++ /dev/null @@ -1,19 +0,0 @@ -// run-rustfix - -#![allow(unused, clippy::assign_op_pattern)] - -fn main() { - - let a = "zero"; - - - - let b = 1; - let c = 2; - - - let d: usize = 1; - - - let e = format!("{}", d); -} diff --git a/tests/ui/needless_late_init_fixable.rs b/tests/ui/needless_late_init_fixable.rs deleted file mode 100644 index 3e6bd36367275..0000000000000 --- a/tests/ui/needless_late_init_fixable.rs +++ /dev/null @@ -1,19 +0,0 @@ -// run-rustfix - -#![allow(unused, clippy::assign_op_pattern)] - -fn main() { - let a; - a = "zero"; - - let b; - let c; - b = 1; - c = 2; - - let d: usize; - d = 1; - - let e; - e = format!("{}", d); -} diff --git a/tests/ui/needless_late_init_fixable.stderr b/tests/ui/needless_late_init_fixable.stderr deleted file mode 100644 index 8c664309e3e83..0000000000000 --- a/tests/ui/needless_late_init_fixable.stderr +++ /dev/null @@ -1,70 +0,0 @@ -error: unneeded late initialization - --> $DIR/needless_late_init_fixable.rs:6:5 - | -LL | let a; - | ^^^^^^ created here -LL | a = "zero"; - | ^^^^^^^^^^ initialised here - | - = note: `-D clippy::needless-late-init` implied by `-D warnings` -help: declare `a` here - | -LL | let a = "zero"; - | ~~~~~ - -error: unneeded late initialization - --> $DIR/needless_late_init_fixable.rs:9:5 - | -LL | let b; - | ^^^^^^ created here -LL | let c; -LL | b = 1; - | ^^^^^ initialised here - | -help: declare `b` here - | -LL | let b = 1; - | ~~~~~ - -error: unneeded late initialization - --> $DIR/needless_late_init_fixable.rs:10:5 - | -LL | let c; - | ^^^^^^ created here -LL | b = 1; -LL | c = 2; - | ^^^^^ initialised here - | -help: declare `c` here - | -LL | let c = 2; - | ~~~~~ - -error: unneeded late initialization - --> $DIR/needless_late_init_fixable.rs:14:5 - | -LL | let d: usize; - | ^^^^^^^^^^^^^ created here -LL | d = 1; - | ^^^^^ initialised here - | -help: declare `d` here - | -LL | let d: usize = 1; - | ~~~~~~~~~~~~ - -error: unneeded late initialization - --> $DIR/needless_late_init_fixable.rs:17:5 - | -LL | let e; - | ^^^^^^ created here -LL | e = format!("{}", d); - | ^^^^^^^^^^^^^^^^^^^^ initialised here - | -help: declare `e` here - | -LL | let e = format!("{}", d); - | ~~~~~ - -error: aborting due to 5 previous errors - diff --git a/tests/ui/unnecessary_cast.fixed b/tests/ui/unnecessary_cast.fixed index e327a38a76d90..b352b285c8626 100644 --- a/tests/ui/unnecessary_cast.fixed +++ b/tests/ui/unnecessary_cast.fixed @@ -1,6 +1,12 @@ // run-rustfix #![warn(clippy::unnecessary_cast)] -#![allow(unused_must_use, clippy::no_effect)] +#![allow( + unused_must_use, + clippy::borrow_as_ptr, + clippy::no_effect, + clippy::nonstandard_macro_braces, + clippy::unnecessary_operation +)] #[rustfmt::skip] fn main() { @@ -38,3 +44,48 @@ fn main() { } type I32Alias = i32; + +mod fixable { + #![allow(dead_code)] + + fn main() { + // casting integer literal to float is unnecessary + 100_f32; + 100_f64; + 100_f64; + let _ = -100_f32; + let _ = -100_f64; + let _ = -100_f64; + 100_f32; + 100_f64; + // Should not trigger + #[rustfmt::skip] + let v = vec!(1); + &v as &[i32]; + 0x10 as f32; + 0o10 as f32; + 0b10 as f32; + 0x11 as f64; + 0o11 as f64; + 0b11 as f64; + + 1_u32; + 0x10_i32; + 0b10_usize; + 0o73_u16; + 1_000_000_000_u32; + + 1.0_f64; + 0.5_f32; + + 1.0 as u16; + + let _ = -1_i32; + let _ = -1.0_f32; + + let _ = 1 as I32Alias; + let _ = &1 as &I32Alias; + } + + type I32Alias = i32; +} diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index 78affa453c0eb..6c8cc3effe8fe 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -1,6 +1,12 @@ // run-rustfix #![warn(clippy::unnecessary_cast)] -#![allow(unused_must_use, clippy::no_effect)] +#![allow( + unused_must_use, + clippy::borrow_as_ptr, + clippy::no_effect, + clippy::nonstandard_macro_braces, + clippy::unnecessary_operation +)] #[rustfmt::skip] fn main() { @@ -38,3 +44,48 @@ fn main() { } type I32Alias = i32; + +mod fixable { + #![allow(dead_code)] + + fn main() { + // casting integer literal to float is unnecessary + 100 as f32; + 100 as f64; + 100_i32 as f64; + let _ = -100 as f32; + let _ = -100 as f64; + let _ = -100_i32 as f64; + 100. as f32; + 100. as f64; + // Should not trigger + #[rustfmt::skip] + let v = vec!(1); + &v as &[i32]; + 0x10 as f32; + 0o10 as f32; + 0b10 as f32; + 0x11 as f64; + 0o11 as f64; + 0b11 as f64; + + 1 as u32; + 0x10 as i32; + 0b10 as usize; + 0o73 as u16; + 1_000_000_000 as u32; + + 1.0 as f64; + 0.5 as f32; + + 1.0 as u16; + + let _ = -1 as i32; + let _ = -1.0 as f32; + + let _ = 1 as I32Alias; + let _ = &1 as &I32Alias; + } + + type I32Alias = i32; +} diff --git a/tests/ui/unnecessary_cast.stderr b/tests/ui/unnecessary_cast.stderr index d1b81ce093778..bad45f0025b22 100644 --- a/tests/ui/unnecessary_cast.stderr +++ b/tests/ui/unnecessary_cast.stderr @@ -1,5 +1,5 @@ error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:8:5 + --> $DIR/unnecessary_cast.rs:14:5 | LL | 1i32 as i32; | ^^^^^^^^^^^ help: try: `1_i32` @@ -7,46 +7,148 @@ LL | 1i32 as i32; = note: `-D clippy::unnecessary-cast` implied by `-D warnings` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:9:5 + --> $DIR/unnecessary_cast.rs:15:5 | LL | 1f32 as f32; | ^^^^^^^^^^^ help: try: `1_f32` error: casting to the same type is unnecessary (`bool` -> `bool`) - --> $DIR/unnecessary_cast.rs:10:5 + --> $DIR/unnecessary_cast.rs:16:5 | LL | false as bool; | ^^^^^^^^^^^^^ help: try: `false` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:13:5 + --> $DIR/unnecessary_cast.rs:19:5 | LL | -1_i32 as i32; | ^^^^^^^^^^^^^ help: try: `-1_i32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:14:5 + --> $DIR/unnecessary_cast.rs:20:5 | LL | - 1_i32 as i32; | ^^^^^^^^^^^^^^ help: try: `- 1_i32` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:15:5 + --> $DIR/unnecessary_cast.rs:21:5 | LL | -1f32 as f32; | ^^^^^^^^^^^^ help: try: `-1_f32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:16:5 + --> $DIR/unnecessary_cast.rs:22:5 | LL | 1_i32 as i32; | ^^^^^^^^^^^^ help: try: `1_i32` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:17:5 + --> $DIR/unnecessary_cast.rs:23:5 | LL | 1_f32 as f32; | ^^^^^^^^^^^^ help: try: `1_f32` -error: aborting due to 8 previous errors +error: casting integer literal to `f32` is unnecessary + --> $DIR/unnecessary_cast.rs:53:9 + | +LL | 100 as f32; + | ^^^^^^^^^^ help: try: `100_f32` + +error: casting integer literal to `f64` is unnecessary + --> $DIR/unnecessary_cast.rs:54:9 + | +LL | 100 as f64; + | ^^^^^^^^^^ help: try: `100_f64` + +error: casting integer literal to `f64` is unnecessary + --> $DIR/unnecessary_cast.rs:55:9 + | +LL | 100_i32 as f64; + | ^^^^^^^^^^^^^^ help: try: `100_f64` + +error: casting integer literal to `f32` is unnecessary + --> $DIR/unnecessary_cast.rs:56:17 + | +LL | let _ = -100 as f32; + | ^^^^^^^^^^^ help: try: `-100_f32` + +error: casting integer literal to `f64` is unnecessary + --> $DIR/unnecessary_cast.rs:57:17 + | +LL | let _ = -100 as f64; + | ^^^^^^^^^^^ help: try: `-100_f64` + +error: casting integer literal to `f64` is unnecessary + --> $DIR/unnecessary_cast.rs:58:17 + | +LL | let _ = -100_i32 as f64; + | ^^^^^^^^^^^^^^^ help: try: `-100_f64` + +error: casting float literal to `f32` is unnecessary + --> $DIR/unnecessary_cast.rs:59:9 + | +LL | 100. as f32; + | ^^^^^^^^^^^ help: try: `100_f32` + +error: casting float literal to `f64` is unnecessary + --> $DIR/unnecessary_cast.rs:60:9 + | +LL | 100. as f64; + | ^^^^^^^^^^^ help: try: `100_f64` + +error: casting integer literal to `u32` is unnecessary + --> $DIR/unnecessary_cast.rs:72:9 + | +LL | 1 as u32; + | ^^^^^^^^ help: try: `1_u32` + +error: casting integer literal to `i32` is unnecessary + --> $DIR/unnecessary_cast.rs:73:9 + | +LL | 0x10 as i32; + | ^^^^^^^^^^^ help: try: `0x10_i32` + +error: casting integer literal to `usize` is unnecessary + --> $DIR/unnecessary_cast.rs:74:9 + | +LL | 0b10 as usize; + | ^^^^^^^^^^^^^ help: try: `0b10_usize` + +error: casting integer literal to `u16` is unnecessary + --> $DIR/unnecessary_cast.rs:75:9 + | +LL | 0o73 as u16; + | ^^^^^^^^^^^ help: try: `0o73_u16` + +error: casting integer literal to `u32` is unnecessary + --> $DIR/unnecessary_cast.rs:76:9 + | +LL | 1_000_000_000 as u32; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32` + +error: casting float literal to `f64` is unnecessary + --> $DIR/unnecessary_cast.rs:78:9 + | +LL | 1.0 as f64; + | ^^^^^^^^^^ help: try: `1.0_f64` + +error: casting float literal to `f32` is unnecessary + --> $DIR/unnecessary_cast.rs:79:9 + | +LL | 0.5 as f32; + | ^^^^^^^^^^ help: try: `0.5_f32` + +error: casting integer literal to `i32` is unnecessary + --> $DIR/unnecessary_cast.rs:83:17 + | +LL | let _ = -1 as i32; + | ^^^^^^^^^ help: try: `-1_i32` + +error: casting float literal to `f32` is unnecessary + --> $DIR/unnecessary_cast.rs:84:17 + | +LL | let _ = -1.0 as f32; + | ^^^^^^^^^^^ help: try: `-1.0_f32` + +error: aborting due to 25 previous errors diff --git a/tests/ui/unnecessary_cast_fixable.fixed b/tests/ui/unnecessary_cast_fixable.fixed deleted file mode 100644 index 36800c5340db2..0000000000000 --- a/tests/ui/unnecessary_cast_fixable.fixed +++ /dev/null @@ -1,50 +0,0 @@ -// run-rustfix - -#![warn(clippy::unnecessary_cast)] -#![allow( - clippy::no_effect, - clippy::unnecessary_operation, - clippy::nonstandard_macro_braces, - clippy::borrow_as_ptr -)] - -fn main() { - // casting integer literal to float is unnecessary - 100_f32; - 100_f64; - 100_f64; - let _ = -100_f32; - let _ = -100_f64; - let _ = -100_f64; - 100_f32; - 100_f64; - // Should not trigger - #[rustfmt::skip] - let v = vec!(1); - &v as &[i32]; - 0x10 as f32; - 0o10 as f32; - 0b10 as f32; - 0x11 as f64; - 0o11 as f64; - 0b11 as f64; - - 1_u32; - 0x10_i32; - 0b10_usize; - 0o73_u16; - 1_000_000_000_u32; - - 1.0_f64; - 0.5_f32; - - 1.0 as u16; - - let _ = -1_i32; - let _ = -1.0_f32; - - let _ = 1 as I32Alias; - let _ = &1 as &I32Alias; -} - -type I32Alias = i32; diff --git a/tests/ui/unnecessary_cast_fixable.rs b/tests/ui/unnecessary_cast_fixable.rs deleted file mode 100644 index d4b6bb952ab35..0000000000000 --- a/tests/ui/unnecessary_cast_fixable.rs +++ /dev/null @@ -1,50 +0,0 @@ -// run-rustfix - -#![warn(clippy::unnecessary_cast)] -#![allow( - clippy::no_effect, - clippy::unnecessary_operation, - clippy::nonstandard_macro_braces, - clippy::borrow_as_ptr -)] - -fn main() { - // casting integer literal to float is unnecessary - 100 as f32; - 100 as f64; - 100_i32 as f64; - let _ = -100 as f32; - let _ = -100 as f64; - let _ = -100_i32 as f64; - 100. as f32; - 100. as f64; - // Should not trigger - #[rustfmt::skip] - let v = vec!(1); - &v as &[i32]; - 0x10 as f32; - 0o10 as f32; - 0b10 as f32; - 0x11 as f64; - 0o11 as f64; - 0b11 as f64; - - 1 as u32; - 0x10 as i32; - 0b10 as usize; - 0o73 as u16; - 1_000_000_000 as u32; - - 1.0 as f64; - 0.5 as f32; - - 1.0 as u16; - - let _ = -1 as i32; - let _ = -1.0 as f32; - - let _ = 1 as I32Alias; - let _ = &1 as &I32Alias; -} - -type I32Alias = i32; diff --git a/tests/ui/unnecessary_cast_fixable.stderr b/tests/ui/unnecessary_cast_fixable.stderr deleted file mode 100644 index a281143281b54..0000000000000 --- a/tests/ui/unnecessary_cast_fixable.stderr +++ /dev/null @@ -1,106 +0,0 @@ -error: casting integer literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:13:5 - | -LL | 100 as f32; - | ^^^^^^^^^^ help: try: `100_f32` - | - = note: `-D clippy::unnecessary-cast` implied by `-D warnings` - -error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:14:5 - | -LL | 100 as f64; - | ^^^^^^^^^^ help: try: `100_f64` - -error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:15:5 - | -LL | 100_i32 as f64; - | ^^^^^^^^^^^^^^ help: try: `100_f64` - -error: casting integer literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:16:13 - | -LL | let _ = -100 as f32; - | ^^^^^^^^^^^ help: try: `-100_f32` - -error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:17:13 - | -LL | let _ = -100 as f64; - | ^^^^^^^^^^^ help: try: `-100_f64` - -error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:18:13 - | -LL | let _ = -100_i32 as f64; - | ^^^^^^^^^^^^^^^ help: try: `-100_f64` - -error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:19:5 - | -LL | 100. as f32; - | ^^^^^^^^^^^ help: try: `100_f32` - -error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:20:5 - | -LL | 100. as f64; - | ^^^^^^^^^^^ help: try: `100_f64` - -error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:32:5 - | -LL | 1 as u32; - | ^^^^^^^^ help: try: `1_u32` - -error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:33:5 - | -LL | 0x10 as i32; - | ^^^^^^^^^^^ help: try: `0x10_i32` - -error: casting integer literal to `usize` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:34:5 - | -LL | 0b10 as usize; - | ^^^^^^^^^^^^^ help: try: `0b10_usize` - -error: casting integer literal to `u16` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:35:5 - | -LL | 0o73 as u16; - | ^^^^^^^^^^^ help: try: `0o73_u16` - -error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:36:5 - | -LL | 1_000_000_000 as u32; - | ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32` - -error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:38:5 - | -LL | 1.0 as f64; - | ^^^^^^^^^^ help: try: `1.0_f64` - -error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:39:5 - | -LL | 0.5 as f32; - | ^^^^^^^^^^ help: try: `0.5_f32` - -error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:43:13 - | -LL | let _ = -1 as i32; - | ^^^^^^^^^ help: try: `-1_i32` - -error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:44:13 - | -LL | let _ = -1.0 as f32; - | ^^^^^^^^^^^ help: try: `-1.0_f32` - -error: aborting due to 17 previous errors - From 8f49e4127fbc64c0275b77f5c24a3da574c14a27 Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Wed, 25 May 2022 07:22:04 -0400 Subject: [PATCH 41/79] Fix `assert!` message --- tests/compile-test.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 18a51020e14ed..4f17e7ffbc299 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -394,8 +394,8 @@ fn check_rustfix_coverage() { .binary_search_by_key(&filename, Path::new) .is_ok(), "`{}` runs `MachineApplicable` diagnostics but is missing a `run-rustfix` annotation. \ - Please either add `// run-rustfix` at the top of file or add the file to \ - `RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS`.", + Please either add `// run-rustfix` at the top of the file or add the file to \ + `RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS` in `tests/compile-test.rs`.", rs_path, ); } From 6027255eb20872b2fada8a8e6c0c9ec5c640e6f4 Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Fri, 27 May 2022 16:38:28 -0400 Subject: [PATCH 42/79] Ignore `crashes` dir --- tests/compile-test.rs | 5 +++-- tests/ui/crashes/ice-7169.fixed | 12 ------------ tests/ui/crashes/ice-7169.rs | 3 --- tests/ui/crashes/ice-7169.stderr | 2 +- tests/ui/crashes/ice-8250.fixed | 7 ------- tests/ui/crashes/ice-8250.rs | 1 - tests/ui/crashes/ice-8250.stderr | 2 +- tests/ui/crashes/ice-8821.fixed | 9 --------- tests/ui/crashes/ice-8821.rs | 1 - tests/ui/crashes/ice-8821.stderr | 2 +- 10 files changed, 6 insertions(+), 38 deletions(-) delete mode 100644 tests/ui/crashes/ice-7169.fixed delete mode 100644 tests/ui/crashes/ice-8250.fixed delete mode 100644 tests/ui/crashes/ice-8821.fixed diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 4f17e7ffbc299..583be5a8d47a1 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -345,8 +345,6 @@ const RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS: &[&str] = &[ "cast_size_32bit.rs", "char_lit_as_u8.rs", "cmp_owned/without_suggestion.rs", - "crashes/ice-6250.rs", - "crashes/ice-6251.rs", "dbg_macro.rs", "deref_addrof_double_trigger.rs", "doc/unbalanced_ticks.rs", @@ -388,6 +386,9 @@ fn check_rustfix_coverage() { assert!(RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS.iter().is_sorted_by_key(Path::new)); for rs_path in missing_coverage_contents.lines() { + if Path::new(rs_path).starts_with("tests/ui/crashes") { + continue; + } let filename = Path::new(rs_path).strip_prefix("tests/ui/").unwrap(); assert!( RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS diff --git a/tests/ui/crashes/ice-7169.fixed b/tests/ui/crashes/ice-7169.fixed deleted file mode 100644 index b36fb49ee7493..0000000000000 --- a/tests/ui/crashes/ice-7169.fixed +++ /dev/null @@ -1,12 +0,0 @@ -// run-rustfix -#![allow(dead_code)] - -#[derive(Default)] -struct A { - a: Vec>, - b: T, -} - -fn main() { - if Ok::<_, ()>(A::::default()).is_ok() {} -} diff --git a/tests/ui/crashes/ice-7169.rs b/tests/ui/crashes/ice-7169.rs index 1769a96bd2f60..82095febc194a 100644 --- a/tests/ui/crashes/ice-7169.rs +++ b/tests/ui/crashes/ice-7169.rs @@ -1,6 +1,3 @@ -// run-rustfix -#![allow(dead_code)] - #[derive(Default)] struct A { a: Vec>, diff --git a/tests/ui/crashes/ice-7169.stderr b/tests/ui/crashes/ice-7169.stderr index e7d0671386b6f..5a9cd32380a19 100644 --- a/tests/ui/crashes/ice-7169.stderr +++ b/tests/ui/crashes/ice-7169.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_ok()` - --> $DIR/ice-7169.rs:11:12 + --> $DIR/ice-7169.rs:8:12 | LL | if let Ok(_) = Ok::<_, ()>(A::::default()) {} | -------^^^^^-------------------------------------- help: try this: `if Ok::<_, ()>(A::::default()).is_ok()` diff --git a/tests/ui/crashes/ice-8250.fixed b/tests/ui/crashes/ice-8250.fixed deleted file mode 100644 index 950dc0b4d5789..0000000000000 --- a/tests/ui/crashes/ice-8250.fixed +++ /dev/null @@ -1,7 +0,0 @@ -// run-rustfix -fn _f(s: &str) -> Option<()> { - let _ = s[1..].split('.').next()?; - Some(()) -} - -fn main() {} diff --git a/tests/ui/crashes/ice-8250.rs b/tests/ui/crashes/ice-8250.rs index d3183d88faee7..d9a5ee1162a49 100644 --- a/tests/ui/crashes/ice-8250.rs +++ b/tests/ui/crashes/ice-8250.rs @@ -1,4 +1,3 @@ -// run-rustfix fn _f(s: &str) -> Option<()> { let _ = s[1..].splitn(2, '.').next()?; Some(()) diff --git a/tests/ui/crashes/ice-8250.stderr b/tests/ui/crashes/ice-8250.stderr index 46d5aa7a2fc03..8ed8f3b3a0642 100644 --- a/tests/ui/crashes/ice-8250.stderr +++ b/tests/ui/crashes/ice-8250.stderr @@ -1,5 +1,5 @@ error: unnecessary use of `splitn` - --> $DIR/ice-8250.rs:3:13 + --> $DIR/ice-8250.rs:2:13 | LL | let _ = s[1..].splitn(2, '.').next()?; | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `s[1..].split('.')` diff --git a/tests/ui/crashes/ice-8821.fixed b/tests/ui/crashes/ice-8821.fixed deleted file mode 100644 index 046b4522ec1a7..0000000000000 --- a/tests/ui/crashes/ice-8821.fixed +++ /dev/null @@ -1,9 +0,0 @@ -// run-rustfix -#![warn(clippy::let_unit_value)] - -fn f() {} -static FN: fn() = f; - -fn main() { - FN(); -} diff --git a/tests/ui/crashes/ice-8821.rs b/tests/ui/crashes/ice-8821.rs index 941225e9d1e51..fb87b79aeed87 100644 --- a/tests/ui/crashes/ice-8821.rs +++ b/tests/ui/crashes/ice-8821.rs @@ -1,4 +1,3 @@ -// run-rustfix #![warn(clippy::let_unit_value)] fn f() {} diff --git a/tests/ui/crashes/ice-8821.stderr b/tests/ui/crashes/ice-8821.stderr index aee13f00ca02c..486096e0a06dd 100644 --- a/tests/ui/crashes/ice-8821.stderr +++ b/tests/ui/crashes/ice-8821.stderr @@ -1,5 +1,5 @@ error: this let-binding has unit value - --> $DIR/ice-8821.rs:8:5 + --> $DIR/ice-8821.rs:7:5 | LL | let _: () = FN(); | ^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `FN();` From 4fc0ee6260d686efc128e44b7b0d0e3d820fff20 Mon Sep 17 00:00:00 2001 From: Caio Date: Fri, 27 May 2022 18:53:02 -0300 Subject: [PATCH 43/79] [1/N] Implement Arithmetic lint --- clippy_lints/src/lib.register_lints.rs | 4 ++-- clippy_lints/src/lib.register_restriction.rs | 4 ++-- clippy_lints/src/lib.rs | 4 ++-- clippy_lints/src/{arithmetic.rs => numeric_arithmetic.rs} | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) rename clippy_lints/src/{arithmetic.rs => numeric_arithmetic.rs} (97%) diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 02171a0909ed7..1fc0a76803d21 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -35,8 +35,6 @@ store.register_lints(&[ utils::internal_lints::UNNECESSARY_SYMBOL_STR, absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS, approx_const::APPROX_CONSTANT, - arithmetic::FLOAT_ARITHMETIC, - arithmetic::INTEGER_ARITHMETIC, as_conversions::AS_CONVERSIONS, asm_syntax::INLINE_ASM_X86_ATT_SYNTAX, asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX, @@ -420,6 +418,8 @@ store.register_lints(&[ non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS, non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY, nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES, + numeric_arithmetic::FLOAT_ARITHMETIC, + numeric_arithmetic::INTEGER_ARITHMETIC, octal_escapes::OCTAL_ESCAPES, only_used_in_recursion::ONLY_USED_IN_RECURSION, open_options::NONSENSICAL_OPEN_OPTIONS, diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs index a6d3a06dc16e3..304b595541aa6 100644 --- a/clippy_lints/src/lib.register_restriction.rs +++ b/clippy_lints/src/lib.register_restriction.rs @@ -3,8 +3,6 @@ // Manual edits will be overwritten. store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ - LintId::of(arithmetic::FLOAT_ARITHMETIC), - LintId::of(arithmetic::INTEGER_ARITHMETIC), LintId::of(as_conversions::AS_CONVERSIONS), LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX), LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX), @@ -50,6 +48,8 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(module_style::MOD_MODULE_FILES), LintId::of(module_style::SELF_NAMED_MODULE_FILES), LintId::of(modulo_arithmetic::MODULO_ARITHMETIC), + LintId::of(numeric_arithmetic::FLOAT_ARITHMETIC), + LintId::of(numeric_arithmetic::INTEGER_ARITHMETIC), LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN), LintId::of(panic_unimplemented::PANIC), LintId::of(panic_unimplemented::TODO), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index ffbf9a6591708..94fca8983eaa7 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -169,7 +169,6 @@ mod renamed_lints; // begin lints modules, do not remove this comment, it’s used in `update_lints` mod absurd_extreme_comparisons; mod approx_const; -mod arithmetic; mod as_conversions; mod asm_syntax; mod assertions_on_constants; @@ -328,6 +327,7 @@ mod non_expressive_names; mod non_octal_unix_permissions; mod non_send_fields_in_send_ty; mod nonstandard_macro_braces; +mod numeric_arithmetic; mod octal_escapes; mod only_used_in_recursion; mod open_options; @@ -678,7 +678,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(doc::DocMarkdown::new(doc_valid_idents.clone()))); store.register_late_pass(|| Box::new(neg_multiply::NegMultiply)); store.register_late_pass(|| Box::new(mem_forget::MemForget)); - store.register_late_pass(|| Box::new(arithmetic::Arithmetic::default())); + store.register_late_pass(|| Box::new(numeric_arithmetic::NumericArithmetic::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(mixed_read_write_in_expression::EvalOrderDependence)); diff --git a/clippy_lints/src/arithmetic.rs b/clippy_lints/src/numeric_arithmetic.rs similarity index 97% rename from clippy_lints/src/arithmetic.rs rename to clippy_lints/src/numeric_arithmetic.rs index c5948707c8124..5c4de3381496c 100644 --- a/clippy_lints/src/arithmetic.rs +++ b/clippy_lints/src/numeric_arithmetic.rs @@ -51,16 +51,16 @@ declare_clippy_lint! { } #[derive(Copy, Clone, Default)] -pub struct Arithmetic { +pub struct NumericArithmetic { expr_span: Option, /// This field is used to check whether expressions are constants, such as in enum discriminants /// and consts const_span: Option, } -impl_lint_pass!(Arithmetic => [INTEGER_ARITHMETIC, FLOAT_ARITHMETIC]); +impl_lint_pass!(NumericArithmetic => [INTEGER_ARITHMETIC, FLOAT_ARITHMETIC]); -impl<'tcx> LateLintPass<'tcx> for Arithmetic { +impl<'tcx> LateLintPass<'tcx> for NumericArithmetic { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if self.expr_span.is_some() { return; From 19f94d52b336a8535c6df1670ed6eb56c08acb55 Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Sat, 28 May 2022 01:36:05 +0200 Subject: [PATCH 44/79] remove `large_enum_variant` suggestion for `Copy` types --- clippy_lints/src/large_enum_variant.rs | 66 ++++++++++++++------------ tests/ui/large_enum_variant.rs | 18 +++++++ tests/ui/large_enum_variant.stderr | 46 +++++++++++++++++- 3 files changed, 99 insertions(+), 31 deletions(-) diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index 0f3889a293618..34b7cf67db1d2 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -1,7 +1,7 @@ //! lint when there is a large size difference between variants on an enum -use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{diagnostics::span_lint_and_then, ty::is_copy}; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -132,37 +132,43 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { let fields = def.variants[variants_size[0].ind].data.fields(); variants_size[0].fields_size.sort_by(|a, b| (a.size.cmp(&b.size))); let mut applicability = Applicability::MaybeIncorrect; - let sugg: Vec<(Span, String)> = variants_size[0] - .fields_size - .iter() - .rev() - .map_while(|val| { - if difference > self.maximum_size_difference_allowed { - difference = difference.saturating_sub(val.size); - Some(( - fields[val.ind].ty.span, - format!( - "Box<{}>", - snippet_with_applicability( - cx, - fields[val.ind].ty.span, - "..", - &mut applicability - ) - .into_owned() - ), - )) - } else { - None - } - }) - .collect(); + if is_copy(cx, ty) { + diag.span_note( + item.ident.span, + "boxing a variant would require the type no longer be `Copy`", + ); + } else { + let sugg: Vec<(Span, String)> = variants_size[0] + .fields_size + .iter() + .rev() + .map_while(|val| { + if difference > self.maximum_size_difference_allowed { + difference = difference.saturating_sub(val.size); + Some(( + fields[val.ind].ty.span, + format!( + "Box<{}>", + snippet_with_applicability( + cx, + fields[val.ind].ty.span, + "..", + &mut applicability + ) + .into_owned() + ), + )) + } else { + None + } + }) + .collect(); - if !sugg.is_empty() { - diag.multipart_suggestion(help_text, sugg, Applicability::MaybeIncorrect); - return; + if !sugg.is_empty() { + diag.multipart_suggestion(help_text, sugg, Applicability::MaybeIncorrect); + return; + } } - diag.span_help(def.variants[variants_size[0].ind].span, help_text); }, ); diff --git a/tests/ui/large_enum_variant.rs b/tests/ui/large_enum_variant.rs index cee9e2372c227..42563383fedb5 100644 --- a/tests/ui/large_enum_variant.rs +++ b/tests/ui/large_enum_variant.rs @@ -98,6 +98,24 @@ struct Struct2 { a: [i32; 8000], } +#[derive(Copy, Clone)] +enum CopyableLargeEnum { + A(bool), + B([u128; 4000]), +} + +enum ManuallyCopyLargeEnum { + A(bool), + B([u128; 4000]), +} + +impl Clone for ManuallyCopyLargeEnum { + fn clone(&self) -> Self { + *self + } +} +impl Copy for ManuallyCopyLargeEnum {} + fn main() { large_enum_variant!(); } diff --git a/tests/ui/large_enum_variant.stderr b/tests/ui/large_enum_variant.stderr index cbf2ac972e2b2..397d263ae9f95 100644 --- a/tests/ui/large_enum_variant.stderr +++ b/tests/ui/large_enum_variant.stderr @@ -127,5 +127,49 @@ help: consider boxing the large fields to reduce the total size of the enum LL | B(Box), | ~~~~~~~~~~~~ -error: aborting due to 8 previous errors +error: large size difference between variants + --> $DIR/large_enum_variant.rs:104:5 + | +LL | B([u128; 4000]), + | ^^^^^^^^^^^^^^^ this variant is 64000 bytes + | +note: and the second-largest variant is 1 bytes: + --> $DIR/large_enum_variant.rs:103:5 + | +LL | A(bool), + | ^^^^^^^ +note: boxing a variant would require the type no longer be `Copy` + --> $DIR/large_enum_variant.rs:102:6 + | +LL | enum CopyableLargeEnum { + | ^^^^^^^^^^^^^^^^^ +help: consider boxing the large fields to reduce the total size of the enum + --> $DIR/large_enum_variant.rs:104:5 + | +LL | B([u128; 4000]), + | ^^^^^^^^^^^^^^^ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:109:5 + | +LL | B([u128; 4000]), + | ^^^^^^^^^^^^^^^ this variant is 64000 bytes + | +note: and the second-largest variant is 1 bytes: + --> $DIR/large_enum_variant.rs:108:5 + | +LL | A(bool), + | ^^^^^^^ +note: boxing a variant would require the type no longer be `Copy` + --> $DIR/large_enum_variant.rs:107:6 + | +LL | enum ManuallyCopyLargeEnum { + | ^^^^^^^^^^^^^^^^^^^^^ +help: consider boxing the large fields to reduce the total size of the enum + --> $DIR/large_enum_variant.rs:109:5 + | +LL | B([u128; 4000]), + | ^^^^^^^^^^^^^^^ + +error: aborting due to 10 previous errors From cfd0f5592b681957a6bcdc33d90cc82a9a76ceff Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sat, 28 May 2022 09:22:59 -0400 Subject: [PATCH 45/79] Make docs more consistent --- clippy_lints/src/approx_const.rs | 2 +- clippy_lints/src/as_conversions.rs | 2 +- clippy_lints/src/assign_ops.rs | 10 ++++-- clippy_lints/src/attrs.rs | 32 ++++++++++--------- clippy_lints/src/blocks_in_if_conditions.rs | 2 +- clippy_lints/src/booleans.rs | 19 ++++++++--- clippy_lints/src/bytecount.rs | 8 ++++- clippy_lints/src/cognitive_complexity.rs | 2 +- clippy_lints/src/collapsible_if.rs | 2 +- clippy_lints/src/comparison_chain.rs | 2 +- clippy_lints/src/copies.rs | 2 +- clippy_lints/src/derivable_impls.rs | 2 +- clippy_lints/src/double_comparison.rs | 2 +- clippy_lints/src/duration_subsec.rs | 18 ++++++----- clippy_lints/src/else_if_without_else.rs | 2 +- clippy_lints/src/empty_enum.rs | 3 +- clippy_lints/src/entry.rs | 2 +- clippy_lints/src/enum_variants.rs | 2 +- clippy_lints/src/equatable_if_let.rs | 2 +- clippy_lints/src/escape.rs | 6 ++-- clippy_lints/src/excessive_bools.rs | 3 +- clippy_lints/src/explicit_write.rs | 10 +++++- clippy_lints/src/fallible_impl_from.rs | 3 +- clippy_lints/src/float_literal.rs | 5 +-- clippy_lints/src/floating_point_arithmetic.rs | 3 +- clippy_lints/src/format.rs | 7 ++-- clippy_lints/src/formatting.rs | 16 +++++++--- clippy_lints/src/loops/mod.rs | 2 +- clippy_lints/src/methods/mod.rs | 6 ++-- 29 files changed, 109 insertions(+), 68 deletions(-) diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs index da1b646f4777a..159f3b0cd014a 100644 --- a/clippy_lints/src/approx_const.rs +++ b/clippy_lints/src/approx_const.rs @@ -28,7 +28,7 @@ declare_clippy_lint! { /// let x = 3.14; /// let y = 1_f64 / x; /// ``` - /// Use predefined constants instead: + /// Use instead: /// ```rust /// let x = std::f32::consts::PI; /// let y = std::f64::consts::FRAC_1_PI; diff --git a/clippy_lints/src/as_conversions.rs b/clippy_lints/src/as_conversions.rs index 88b91d589074d..5fa8934c71bdd 100644 --- a/clippy_lints/src/as_conversions.rs +++ b/clippy_lints/src/as_conversions.rs @@ -29,7 +29,7 @@ declare_clippy_lint! { /// f(a as u16); /// ``` /// - /// Usually better represents the semantics you expect: + /// Use instead: /// ```rust,ignore /// f(a.try_into()?); /// ``` diff --git a/clippy_lints/src/assign_ops.rs b/clippy_lints/src/assign_ops.rs index 4c2d3366483af..f81da2d422333 100644 --- a/clippy_lints/src/assign_ops.rs +++ b/clippy_lints/src/assign_ops.rs @@ -27,10 +27,16 @@ declare_clippy_lint! { /// let mut a = 5; /// let b = 0; /// // ... - /// // Bad + /// /// a = a + b; + /// ``` + /// + /// Use instead: + /// ```rust + /// let mut a = 5; + /// let b = 0; + /// // ... /// - /// // Good /// a += b; /// ``` #[clippy::version = "pre 1.29.0"] diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 7105ce4b292ae..79a5b670fc8ed 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -89,13 +89,14 @@ declare_clippy_lint! { /// /// ### Example /// ```ignore - /// // Bad /// #[deny(dead_code)] /// extern crate foo; /// #[forbid(dead_code)] /// use foo::bar; + /// ``` /// - /// // Ok + /// Use instead: + /// ```rust,ignore /// #[allow(unused_imports)] /// use foo::baz; /// #[allow(unused_imports)] @@ -146,15 +147,19 @@ declare_clippy_lint! { /// /// ### Example /// ```rust + /// #[allow(dead_code)] + /// + /// fn not_quite_good_code() { } + /// ``` + /// + /// Use instead: + /// ```rust /// // Good (as inner attribute) /// #![allow(dead_code)] /// /// fn this_is_fine() { } /// - /// // Bad - /// #[allow(dead_code)] - /// - /// fn not_quite_good_code() { } + /// // or /// /// // Good (as outer attribute) /// #[allow(dead_code)] @@ -175,12 +180,11 @@ declare_clippy_lint! { /// These lints should only be enabled on a lint-by-lint basis and with careful consideration. /// /// ### Example - /// Bad: /// ```rust /// #![deny(clippy::restriction)] /// ``` /// - /// Good: + /// Use instead: /// ```rust /// #![deny(clippy::as_conversions)] /// ``` @@ -205,13 +209,12 @@ declare_clippy_lint! { /// [#3123](https://github.com/rust-lang/rust-clippy/pull/3123#issuecomment-422321765) /// /// ### Example - /// Bad: /// ```rust /// #[cfg_attr(rustfmt, rustfmt_skip)] /// fn main() { } /// ``` /// - /// Good: + /// Use instead: /// ```rust /// #[rustfmt::skip] /// fn main() { } @@ -231,19 +234,19 @@ declare_clippy_lint! { /// by the conditional compilation engine. /// /// ### Example - /// Bad: /// ```rust /// #[cfg(linux)] /// fn conditional() { } /// ``` /// - /// Good: + /// Use instead: /// ```rust /// #[cfg(target_os = "linux")] /// fn conditional() { } /// ``` /// - /// Or: + /// or + /// /// ```rust /// #[cfg(unix)] /// fn conditional() { } @@ -266,14 +269,13 @@ declare_clippy_lint! { /// ensure that others understand the reasoning /// /// ### Example - /// Bad: /// ```rust /// #![feature(lint_reasons)] /// /// #![allow(clippy::some_lint)] /// ``` /// - /// Good: + /// Use instead: /// ```rust /// #![feature(lint_reasons)] /// diff --git a/clippy_lints/src/blocks_in_if_conditions.rs b/clippy_lints/src/blocks_in_if_conditions.rs index 4c4dd85d518a6..86e28ea7f7a92 100644 --- a/clippy_lints/src/blocks_in_if_conditions.rs +++ b/clippy_lints/src/blocks_in_if_conditions.rs @@ -29,7 +29,7 @@ declare_clippy_lint! { /// if true { /* ... */ } /// ``` /// - /// // or + /// or /// /// ```rust /// # fn somefunc() -> bool { true }; diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 0adb6327164e7..e4e122ba6eb59 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -27,8 +27,14 @@ declare_clippy_lint! { /// /// ### Example /// ```ignore - /// if a && true // should be: if a - /// if !(a == b) // should be: if a != b + /// if a && true {} + /// if !(a == b) {} + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// if a {} + /// if a != b {} /// ``` #[clippy::version = "pre 1.29.0"] pub NONMINIMAL_BOOL, @@ -48,10 +54,15 @@ declare_clippy_lint! { /// Ignores short circuiting behavior. /// /// ### Example - /// ```ignore + /// ```rust,ignore + /// // The `b` is unnecessary, the expression is equivalent to `if a`. /// if a && b || a { ... } /// ``` - /// The `b` is unnecessary, the expression is equivalent to `if a`. + /// + /// Use instead: + /// ```rust,ignore + /// if a {} + /// ``` #[clippy::version = "pre 1.29.0"] pub LOGIC_BUG, correctness, diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs index 02d97bf43df83..bfdbaf2413a25 100644 --- a/clippy_lints/src/bytecount.rs +++ b/clippy_lints/src/bytecount.rs @@ -28,7 +28,13 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # let vec = vec![1_u8]; - /// &vec.iter().filter(|x| **x == 0u8).count(); // use bytecount::count instead + /// let count = vec.iter().filter(|x| **x == 0u8).count(); + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// # let vec = vec![1_u8]; + /// let count = bytecount::count(&vec, 0u8); /// ``` #[clippy::version = "pre 1.29.0"] pub NAIVE_BYTECOUNT, diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 317c4bfb3226e..33c44f8b2dba9 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -25,7 +25,7 @@ declare_clippy_lint! { /// complexity. /// /// ### Example - /// No. You'll see it when you get the warning. + /// You'll see it when you get the warning. #[clippy::version = "1.35.0"] pub COGNITIVE_COMPLEXITY, nursery, diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index 3227e6e86af4e..3eceb848822e9 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -41,7 +41,7 @@ declare_clippy_lint! { /// /// ``` /// - /// Should be written: + /// Use instead: /// /// ```rust,ignore /// if x && y { diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index 399d11472b09b..913e081af3bda 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -34,7 +34,7 @@ declare_clippy_lint! { /// } /// ``` /// - /// Could be written: + /// Use instead: /// /// ```rust,ignore /// use std::cmp::Ordering; diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index e6a0162fd0272..1e9a115301100 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -141,7 +141,7 @@ declare_clippy_lint! { /// }; /// ``` /// - /// Could be written as: + /// Use instead: /// ```ignore /// println!("Hello World"); /// let foo = if … { diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs index 34a5f8444dea0..e98691fd5bb0a 100644 --- a/clippy_lints/src/derivable_impls.rs +++ b/clippy_lints/src/derivable_impls.rs @@ -21,7 +21,7 @@ declare_clippy_lint! { /// bar: bool /// } /// - /// impl std::default::Default for Foo { + /// impl Default for Foo { /// fn default() -> Self { /// Self { /// bar: false diff --git a/clippy_lints/src/double_comparison.rs b/clippy_lints/src/double_comparison.rs index be95375789d5b..ee0440e52ff85 100644 --- a/clippy_lints/src/double_comparison.rs +++ b/clippy_lints/src/double_comparison.rs @@ -24,7 +24,7 @@ declare_clippy_lint! { /// if x == y || x < y {} /// ``` /// - /// Could be written as: + /// Use instead: /// /// ```rust /// # let x = 1; diff --git a/clippy_lints/src/duration_subsec.rs b/clippy_lints/src/duration_subsec.rs index 09318f74527c2..d85ace3a279b3 100644 --- a/clippy_lints/src/duration_subsec.rs +++ b/clippy_lints/src/duration_subsec.rs @@ -22,15 +22,17 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # use std::time::Duration; - /// let dur = Duration::new(5, 0); - /// - /// // Bad - /// let _micros = dur.subsec_nanos() / 1_000; - /// let _millis = dur.subsec_nanos() / 1_000_000; + /// # let duration = Duration::new(5, 0); + /// let micros = duration.subsec_nanos() / 1_000; + /// let millis = duration.subsec_nanos() / 1_000_000; + /// ``` /// - /// // Good - /// let _micros = dur.subsec_micros(); - /// let _millis = dur.subsec_millis(); + /// Use instead: + /// ```rust + /// # use std::time::Duration; + /// # let duration = Duration::new(5, 0); + /// let micros = duration.subsec_micros(); + /// let millis = duration.subsec_millis(); /// ``` #[clippy::version = "pre 1.29.0"] pub DURATION_SUBSEC, diff --git a/clippy_lints/src/else_if_without_else.rs b/clippy_lints/src/else_if_without_else.rs index 0b9f54231c59b..bf4488570eaf2 100644 --- a/clippy_lints/src/else_if_without_else.rs +++ b/clippy_lints/src/else_if_without_else.rs @@ -26,7 +26,7 @@ declare_clippy_lint! { /// } /// ``` /// - /// Could be written: + /// Use instead: /// /// ```rust /// # fn a() {} diff --git a/clippy_lints/src/empty_enum.rs b/clippy_lints/src/empty_enum.rs index b5d6b3c7524ba..bbebc02441412 100644 --- a/clippy_lints/src/empty_enum.rs +++ b/clippy_lints/src/empty_enum.rs @@ -23,12 +23,11 @@ declare_clippy_lint! { /// /// /// ### Example - /// Bad: /// ```rust /// enum Test {} /// ``` /// - /// Good: + /// Use instead: /// ```rust /// #![feature(never_type)] /// diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index c5a987842c3f1..27743a0ebec7e 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -46,7 +46,7 @@ declare_clippy_lint! { /// map.insert(k, v); /// } /// ``` - /// can both be rewritten as: + /// Use instead: /// ```rust /// # use std::collections::HashMap; /// # let mut map = HashMap::new(); diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index e029b8e85379f..263a5b573c9cf 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -32,7 +32,7 @@ declare_clippy_lint! { /// BattenbergCake, /// } /// ``` - /// Could be written as: + /// Use instead: /// ```rust /// enum Cake { /// BlackForest, diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs index cf47e581ccb48..ef1216358dd97 100644 --- a/clippy_lints/src/equatable_if_let.rs +++ b/clippy_lints/src/equatable_if_let.rs @@ -26,7 +26,7 @@ declare_clippy_lint! { /// do_thing(); /// } /// ``` - /// Should be written + /// Use instead: /// ```rust,ignore /// if x == Some(2) { /// do_thing(); diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index af591dd71aa1d..b0854ed9bbb45 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -31,12 +31,14 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # fn foo(bar: usize) {} - /// // Bad /// let x = Box::new(1); /// foo(*x); /// println!("{}", *x); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # fn foo(bar: usize) {} /// let x = 1; /// foo(x); /// println!("{}", x); diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 7a81fb37e841c..a2af10e2ba5ea 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -18,7 +18,6 @@ declare_clippy_lint! { /// readability and API. /// /// ### Example - /// Bad: /// ```rust /// struct S { /// is_pending: bool, @@ -27,7 +26,7 @@ declare_clippy_lint! { /// } /// ``` /// - /// Good: + /// Use instead: /// ```rust /// enum S { /// Pending, diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index 3e2217c28da3a..6b9805efbcfc2 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -21,8 +21,16 @@ declare_clippy_lint! { /// ```rust /// # use std::io::Write; /// # let bar = "furchtbar"; - /// // this would be clearer as `eprintln!("foo: {:?}", bar);` /// writeln!(&mut std::io::stderr(), "foo: {:?}", bar).unwrap(); + /// writeln!(&mut std::io::stdout(), "foo: {:?}", bar).unwrap(); + /// ``` + /// + /// Use instead: + /// ```rust + /// # use std::io::Write; + /// # let bar = "furchtbar"; + /// eprintln!("foo: {:?}", bar); + /// println!("foo: {:?}", bar); /// ``` #[clippy::version = "pre 1.29.0"] pub EXPLICIT_WRITE, diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index 9f868df3ad063..b88e53aeca693 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -20,7 +20,6 @@ declare_clippy_lint! { /// ```rust /// struct Foo(i32); /// - /// // Bad /// impl From for Foo { /// fn from(s: String) -> Self { /// Foo(s.parse().unwrap()) @@ -28,8 +27,8 @@ declare_clippy_lint! { /// } /// ``` /// + /// Use instead: /// ```rust - /// // Good /// struct Foo(i32); /// /// impl TryFrom for Foo { diff --git a/clippy_lints/src/float_literal.rs b/clippy_lints/src/float_literal.rs index 7a4397a7b7467..f850ea31f4d6e 100644 --- a/clippy_lints/src/float_literal.rs +++ b/clippy_lints/src/float_literal.rs @@ -19,11 +19,12 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// let v: f32 = 0.123_456_789_9; /// println!("{}", v); // 0.123_456_789 + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// let v: f64 = 0.123_456_789_9; /// println!("{}", v); // 0.123_456_789_9 /// ``` diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 42503c26de1d1..df9b41d2c98be 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -35,8 +35,7 @@ declare_clippy_lint! { /// let _ = a.exp() - 1.0; /// ``` /// - /// is better expressed as - /// + /// Use instead: /// ```rust /// let a = 3f32; /// let _ = a.cbrt(); diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index 64c41b565878b..3084c70589fa3 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -25,12 +25,13 @@ declare_clippy_lint! { /// /// ### Examples /// ```rust - /// - /// // Bad /// let foo = "foo"; /// format!("{}", foo); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// let foo = "foo"; /// foo.to_owned(); /// ``` #[clippy::version = "pre 1.29.0"] diff --git a/clippy_lints/src/formatting.rs b/clippy_lints/src/formatting.rs index 57964b8d48ea9..db0166da57f0e 100644 --- a/clippy_lints/src/formatting.rs +++ b/clippy_lints/src/formatting.rs @@ -36,12 +36,18 @@ declare_clippy_lint! { /// This is either a typo in the binary operator or confusing. /// /// ### Example - /// ```rust,ignore - /// if foo <- 30 { // this should be `foo < -30` but looks like a different operator - /// } + /// ```rust + /// # let foo = true; + /// # let bar = false; + /// // &&! looks like a different operator + /// if foo &&! bar {} + /// ``` /// - /// if foo &&! bar { // this should be `foo && !bar` but looks like a different operator - /// } + /// Use instead: + /// ```rust + /// # let foo = true; + /// # let bar = false; + /// if foo && !bar {} /// ``` #[clippy::version = "1.40.0"] pub SUSPICIOUS_UNARY_OP_FORMATTING, diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 75d771f992a8c..417248e31ae8c 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -192,7 +192,7 @@ declare_clippy_lint! { /// } /// ``` /// - /// // or + /// or /// /// ```rust /// # let res: Result = Ok(1); diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index b820b74093045..eb1bce8c7d25b 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -203,7 +203,7 @@ declare_clippy_lint! { /// opt.expect("more helpful message"); /// ``` /// - /// // or + /// or /// /// ```rust /// # let res: Result = Ok(1); @@ -245,7 +245,7 @@ declare_clippy_lint! { /// opt?; /// ``` /// - /// // or + /// or /// /// ```rust /// # let res: Result = Ok(1); @@ -440,7 +440,7 @@ declare_clippy_lint! { /// x.map_or(0, |a| a + 1); /// ``` /// - /// // or + /// or /// /// ```rust /// # let x: Result = Ok(1); From adafb6c41620b91ebd131c2c893f7f13caf9b60c Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sat, 28 May 2022 09:50:14 -0400 Subject: [PATCH 46/79] Update lint creation docs --- doc/adding_lints.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/adding_lints.md b/doc/adding_lints.md index e8f0c338fd58a..3e0b1c5c4f782 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -516,11 +516,12 @@ declare_clippy_lint! { /// ### Example /// /// ```rust,ignore - /// // Bad - /// Insert a short example of code that triggers the lint - /// - /// // Good - /// Insert a short example of improved code that doesn't trigger the lint + /// // A short example of code that triggers the lint + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// // A short example of improved code that doesn't trigger the lint /// ``` #[clippy::version = "1.29.0"] pub FOO_FUNCTIONS, From 722f7d28c3daf2d568a3af77a62c489fb6a1aea2 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Sun, 29 May 2022 18:28:34 +0000 Subject: [PATCH 47/79] needless_late_init: fix ICE when all branches return the never type --- clippy_lints/src/needless_late_init.rs | 13 +- tests/ui/needless_late_init.fixed | 47 +++++--- tests/ui/needless_late_init.rs | 47 +++++--- tests/ui/needless_late_init.stderr | 158 ++++++++++++------------- 4 files changed, 148 insertions(+), 117 deletions(-) diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index b70871b38beab..26c694a71fedd 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -194,14 +194,15 @@ fn assignment_suggestions<'tcx>( })) .collect::>>()?; - let applicability = if suggestions.len() > 1 { + match suggestions.len() { + // All of `exprs` are never types + // https://github.com/rust-lang/rust-clippy/issues/8911 + 0 => None, + 1 => Some((Applicability::MachineApplicable, suggestions)), // multiple suggestions don't work with rustfix in multipart_suggest // https://github.com/rust-lang/rustfix/issues/141 - Applicability::Unspecified - } else { - Applicability::MachineApplicable - }; - Some((applicability, suggestions)) + _ => Some((Applicability::Unspecified, suggestions)), + } } struct Usage<'tcx> { diff --git a/tests/ui/needless_late_init.fixed b/tests/ui/needless_late_init.fixed index 63ae6e389f3d1..fee8e3030b808 100644 --- a/tests/ui/needless_late_init.fixed +++ b/tests/ui/needless_late_init.fixed @@ -3,6 +3,7 @@ #![allow( unused, clippy::assign_op_pattern, + clippy::blocks_in_if_conditions, clippy::let_and_return, clippy::let_unit_value, clippy::nonminimal_bool @@ -18,6 +19,22 @@ impl std::ops::Drop for SignificantDrop { } } +fn simple() { + + let a = "zero"; + + + + let b = 1; + let c = 2; + + + let d: usize = 1; + + + let e = format!("{}", d); +} + fn main() { let n = 1; @@ -237,22 +254,20 @@ fn does_not_lint() { x = SignificantDrop; } -mod fixable { - #![allow(dead_code)] - - fn main() { - - let a = "zero"; - - - - let b = 1; - let c = 2; - - - let d: usize = 1; +#[rustfmt::skip] +fn issue8911() -> u32 { + let x; + match 1 { + _ if { x = 1; false } => return 1, + _ => return 2, + } - - let e = format!("{}", d); + let x; + if { x = 1; true } { + return 1; + } else { + return 2; } + + 3 } diff --git a/tests/ui/needless_late_init.rs b/tests/ui/needless_late_init.rs index 89a85c8e053cd..402d9f9ef7f81 100644 --- a/tests/ui/needless_late_init.rs +++ b/tests/ui/needless_late_init.rs @@ -3,6 +3,7 @@ #![allow( unused, clippy::assign_op_pattern, + clippy::blocks_in_if_conditions, clippy::let_and_return, clippy::let_unit_value, clippy::nonminimal_bool @@ -18,6 +19,22 @@ impl std::ops::Drop for SignificantDrop { } } +fn simple() { + let a; + a = "zero"; + + let b; + let c; + b = 1; + c = 2; + + let d: usize; + d = 1; + + let e; + e = format!("{}", d); +} + fn main() { let a; let n = 1; @@ -237,22 +254,20 @@ fn does_not_lint() { x = SignificantDrop; } -mod fixable { - #![allow(dead_code)] - - fn main() { - let a; - a = "zero"; - - let b; - let c; - b = 1; - c = 2; - - let d: usize; - d = 1; +#[rustfmt::skip] +fn issue8911() -> u32 { + let x; + match 1 { + _ if { x = 1; false } => return 1, + _ => return 2, + } - let e; - e = format!("{}", d); + let x; + if { x = 1; true } { + return 1; + } else { + return 2; } + + 3 } diff --git a/tests/ui/needless_late_init.stderr b/tests/ui/needless_late_init.stderr index d17666e03ed20..f320b5b9cbb37 100644 --- a/tests/ui/needless_late_init.stderr +++ b/tests/ui/needless_late_init.stderr @@ -1,12 +1,79 @@ error: unneeded late initialization - --> $DIR/needless_late_init.rs:22:5 + --> $DIR/needless_late_init.rs:23:5 | LL | let a; - | ^^^^^^ + | ^^^^^^ created here +LL | a = "zero"; + | ^^^^^^^^^^ initialised here | = note: `-D clippy::needless-late-init` implied by `-D warnings` help: declare `a` here | +LL | let a = "zero"; + | ~~~~~ + +error: unneeded late initialization + --> $DIR/needless_late_init.rs:26:5 + | +LL | let b; + | ^^^^^^ created here +LL | let c; +LL | b = 1; + | ^^^^^ initialised here + | +help: declare `b` here + | +LL | let b = 1; + | ~~~~~ + +error: unneeded late initialization + --> $DIR/needless_late_init.rs:27:5 + | +LL | let c; + | ^^^^^^ created here +LL | b = 1; +LL | c = 2; + | ^^^^^ initialised here + | +help: declare `c` here + | +LL | let c = 2; + | ~~~~~ + +error: unneeded late initialization + --> $DIR/needless_late_init.rs:31:5 + | +LL | let d: usize; + | ^^^^^^^^^^^^^ created here +LL | d = 1; + | ^^^^^ initialised here + | +help: declare `d` here + | +LL | let d: usize = 1; + | ~~~~~~~~~~~~ + +error: unneeded late initialization + --> $DIR/needless_late_init.rs:34:5 + | +LL | let e; + | ^^^^^^ created here +LL | e = format!("{}", d); + | ^^^^^^^^^^^^^^^^^^^^ initialised here + | +help: declare `e` here + | +LL | let e = format!("{}", d); + | ~~~~~ + +error: unneeded late initialization + --> $DIR/needless_late_init.rs:39:5 + | +LL | let a; + | ^^^^^^ + | +help: declare `a` here + | LL | let a = match n { | +++++++ help: remove the assignments from the `match` arms @@ -21,7 +88,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:31:5 + --> $DIR/needless_late_init.rs:48:5 | LL | let b; | ^^^^^^ @@ -42,7 +109,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:38:5 + --> $DIR/needless_late_init.rs:55:5 | LL | let d; | ^^^^^^ @@ -63,7 +130,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:46:5 + --> $DIR/needless_late_init.rs:63:5 | LL | let e; | ^^^^^^ @@ -84,7 +151,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:53:5 + --> $DIR/needless_late_init.rs:70:5 | LL | let f; | ^^^^^^ @@ -100,7 +167,7 @@ LL + 1 => "three", | error: unneeded late initialization - --> $DIR/needless_late_init.rs:59:5 + --> $DIR/needless_late_init.rs:76:5 | LL | let g: usize; | ^^^^^^^^^^^^^ @@ -120,7 +187,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:67:5 + --> $DIR/needless_late_init.rs:84:5 | LL | let x; | ^^^^^^ created here @@ -134,7 +201,7 @@ LL | let x = 1; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:71:5 + --> $DIR/needless_late_init.rs:88:5 | LL | let x; | ^^^^^^ created here @@ -148,7 +215,7 @@ LL | let x = SignificantDrop; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:75:5 + --> $DIR/needless_late_init.rs:92:5 | LL | let x; | ^^^^^^ created here @@ -162,7 +229,7 @@ LL | let x = SignificantDrop; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:94:5 + --> $DIR/needless_late_init.rs:111:5 | LL | let a; | ^^^^^^ @@ -183,7 +250,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:111:5 + --> $DIR/needless_late_init.rs:128:5 | LL | let a; | ^^^^^^ @@ -203,72 +270,5 @@ help: add a semicolon after the `match` expression LL | }; | + -error: unneeded late initialization - --> $DIR/needless_late_init.rs:244:9 - | -LL | let a; - | ^^^^^^ created here -LL | a = "zero"; - | ^^^^^^^^^^ initialised here - | -help: declare `a` here - | -LL | let a = "zero"; - | ~~~~~ - -error: unneeded late initialization - --> $DIR/needless_late_init.rs:247:9 - | -LL | let b; - | ^^^^^^ created here -LL | let c; -LL | b = 1; - | ^^^^^ initialised here - | -help: declare `b` here - | -LL | let b = 1; - | ~~~~~ - -error: unneeded late initialization - --> $DIR/needless_late_init.rs:248:9 - | -LL | let c; - | ^^^^^^ created here -LL | b = 1; -LL | c = 2; - | ^^^^^ initialised here - | -help: declare `c` here - | -LL | let c = 2; - | ~~~~~ - -error: unneeded late initialization - --> $DIR/needless_late_init.rs:252:9 - | -LL | let d: usize; - | ^^^^^^^^^^^^^ created here -LL | d = 1; - | ^^^^^ initialised here - | -help: declare `d` here - | -LL | let d: usize = 1; - | ~~~~~~~~~~~~ - -error: unneeded late initialization - --> $DIR/needless_late_init.rs:255:9 - | -LL | let e; - | ^^^^^^ created here -LL | e = format!("{}", d); - | ^^^^^^^^^^^^^^^^^^^^ initialised here - | -help: declare `e` here - | -LL | let e = format!("{}", d); - | ~~~~~ - error: aborting due to 16 previous errors From c5410150eb5563ed1f0529bf16e0524846a8979d Mon Sep 17 00:00:00 2001 From: lyj Date: Sun, 29 May 2022 09:57:15 +0800 Subject: [PATCH 48/79] needless_deref --- CHANGELOG.md | 1 + clippy_lints/src/borrow_deref_ref.rs | 118 ++++++++++++++++++ clippy_lints/src/checked_conversions.rs | 4 +- clippy_lints/src/let_if_seq.rs | 2 +- clippy_lints/src/lib.register_all.rs | 1 + clippy_lints/src/lib.register_complexity.rs | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/loops/never_loop.rs | 2 +- clippy_lints/src/matches/match_bool.rs | 4 +- .../src/methods/unnecessary_to_owned.rs | 2 +- .../src/mixed_read_write_in_expression.rs | 2 +- clippy_lints/src/mut_key.rs | 2 +- clippy_lints/src/panic_in_result_fn.rs | 2 +- clippy_lints/src/pass_by_ref_or_value.rs | 2 +- clippy_lints/src/redundant_clone.rs | 6 +- .../src/redundant_static_lifetimes.rs | 9 +- clippy_lints/src/size_of_in_element_count.rs | 2 +- clippy_lints/src/utils/internal_lints.rs | 8 +- .../internal_lints/metadata_collector.rs | 3 +- clippy_utils/src/consts.rs | 2 +- clippy_utils/src/higher.rs | 2 +- clippy_utils/src/hir_utils.rs | 6 +- clippy_utils/src/lib.rs | 4 +- .../ui-internal/unnecessary_symbol_str.fixed | 1 + tests/ui-internal/unnecessary_symbol_str.rs | 1 + .../ui-internal/unnecessary_symbol_str.stderr | 10 +- tests/ui/borrow_deref_ref.rs | 58 +++++++++ tests/ui/borrow_deref_ref.stderr | 37 ++++++ tests/ui/collapsible_match2.rs | 2 +- tests/ui/collapsible_match2.stderr | 4 +- tests/ui/deref_by_slicing.fixed | 1 + tests/ui/deref_by_slicing.rs | 1 + tests/ui/deref_by_slicing.stderr | 18 +-- tests/ui/explicit_deref_methods.fixed | 7 +- tests/ui/explicit_deref_methods.rs | 7 +- tests/ui/explicit_deref_methods.stderr | 24 ++-- tests/ui/forget_ref.rs | 1 + tests/ui/forget_ref.stderr | 36 +++--- tests/ui/recursive_format_impl.rs | 3 +- tests/ui/recursive_format_impl.stderr | 20 +-- 41 files changed, 325 insertions(+), 94 deletions(-) create mode 100644 clippy_lints/src/borrow_deref_ref.rs create mode 100644 tests/ui/borrow_deref_ref.rs create mode 100644 tests/ui/borrow_deref_ref.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 3aaa79a1719c2..ece80343622f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3296,6 +3296,7 @@ Released 2018-09-13 [`bool_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison [`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison [`borrow_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr +[`borrow_deref_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_deref_ref [`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const [`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box [`box_collection`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_collection diff --git a/clippy_lints/src/borrow_deref_ref.rs b/clippy_lints/src/borrow_deref_ref.rs new file mode 100644 index 0000000000000..ec2f31cf67374 --- /dev/null +++ b/clippy_lints/src/borrow_deref_ref.rs @@ -0,0 +1,118 @@ +use crate::reference::DEREF_ADDROF; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_opt; +use clippy_utils::ty::implements_trait; +use clippy_utils::{get_parent_expr, is_lint_allowed}; +use rustc_errors::Applicability; +use rustc_hir::{ExprKind, UnOp}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::Mutability; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for `&*(&T)`. + /// + /// ### Why is this bad? + /// Dereferencing and then borrowing a reference value has no effect in most cases. + /// + /// ### Known problems + /// false negative on such code: + /// ``` + /// let x = &12; + /// let addr_x = &x as *const _ as usize; + /// let addr_y = &&*x as *const _ as usize; // assert ok now, and lint triggerd. + /// // But if we fix it, assert will fail. + /// assert_ne!(addr_x, addr_y); + /// ``` + /// + /// ### Example + /// ```rust + /// let s = &String::new(); + /// + /// // Bad + /// let a: &String = &* s; + /// foo(&*s); + /// + /// // Good + /// let a: &String = s; + /// foo(&**s); + /// + /// fn foo(_: &str){ } + /// ``` + #[clippy::version = "1.59.0"] + pub BORROW_DEREF_REF, + complexity, + "deref on an immutable reference returns the same type as itself" +} + +declare_lint_pass!(BorrowDerefRef => [BORROW_DEREF_REF]); + +impl LateLintPass<'_> for BorrowDerefRef { + fn check_expr(&mut self, cx: &LateContext<'_>, e: &rustc_hir::Expr<'_>) { + if_chain! { + if !e.span.from_expansion(); + if let ExprKind::AddrOf(_, Mutability::Not, addrof_target) = e.kind; + if !addrof_target.span.from_expansion(); + if let ExprKind::Unary(UnOp::Deref, deref_target) = addrof_target.kind; + if !deref_target.span.from_expansion(); + if !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..) ); + let ref_ty = cx.typeck_results().expr_ty(deref_target); + if let ty::Ref(_, inner_ty, Mutability::Not) = ref_ty.kind(); + then{ + + if let Some(parent_expr) = get_parent_expr(cx, e){ + if matches!(parent_expr.kind, ExprKind::Unary(UnOp::Deref, ..)) && + !is_lint_allowed(cx, DEREF_ADDROF, parent_expr.hir_id) { + return; + } + + // modification to `&mut &*x` is different from `&mut x` + if matches!(deref_target.kind, ExprKind::Path(..) + | ExprKind::Field(..) + | ExprKind::Index(..) + | ExprKind::Unary(UnOp::Deref, ..)) + && matches!(parent_expr.kind, ExprKind::AddrOf(_, Mutability::Mut, _)) { + return; + } + } + + span_lint_and_then( + cx, + BORROW_DEREF_REF, + e.span, + "deref on an immutable reference", + |diag| { + diag.span_suggestion( + e.span, + "if you would like to reborrow, try removing `&*`", + snippet_opt(cx, deref_target.span).unwrap(), + Applicability::MachineApplicable + ); + + // has deref trait -> give 2 help + // doesn't have deref trait -> give 1 help + if let Some(deref_trait_id) = cx.tcx.lang_items().deref_trait(){ + if !implements_trait(cx, *inner_ty, deref_trait_id, &[]) { + return; + } + } + + diag.span_suggestion( + e.span, + "if you would like to deref, try using `&**`", + format!( + "&**{}", + &snippet_opt(cx, deref_target.span).unwrap(), + ), + Applicability::MaybeIncorrect + ); + + } + ); + + } + } + } +} diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 7eeaaa0192147..1010340c71213 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -318,7 +318,7 @@ fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function: if let QPath::TypeRelative(ty, path) = &path; if path.ident.name.as_str() == function; if let TyKind::Path(QPath::Resolved(None, tp)) = &ty.kind; - if let [int] = &*tp.segments; + if let [int] = tp.segments; then { let name = int.ident.name.as_str(); candidates.iter().find(|c| &name == *c).copied() @@ -332,7 +332,7 @@ fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function: fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> { if_chain! { if let QPath::Resolved(_, path) = *path; - if let [ty] = &*path.segments; + if let [ty] = path.segments; then { let name = ty.ident.name.as_str(); INTS.iter().find(|c| &name == *c).copied() diff --git a/clippy_lints/src/let_if_seq.rs b/clippy_lints/src/let_if_seq.rs index db09d00d7303f..56bbbbbc819e5 100644 --- a/clippy_lints/src/let_if_seq.rs +++ b/clippy_lints/src/let_if_seq.rs @@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq { if let hir::ExprKind::If(hir::Expr { kind: hir::ExprKind::DropTemps(cond), ..}, then, else_) = if_.kind; if !is_local_used(cx, *cond, canonical_id); if let hir::ExprKind::Block(then, _) = then.kind; - if let Some(value) = check_assign(cx, canonical_id, &*then); + if let Some(value) = check_assign(cx, canonical_id, then); if !is_local_used(cx, value, canonical_id); then { let span = stmt.span.to(if_.span); diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index a028b41db7740..de43d4be1b38c 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -24,6 +24,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON), LintId::of(booleans::LOGIC_BUG), LintId::of(booleans::NONMINIMAL_BOOL), + LintId::of(borrow_deref_ref::BORROW_DEREF_REF), LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN), LintId::of(casts::CAST_ABS_TO_UNSIGNED), LintId::of(casts::CAST_ENUM_CONSTRUCTOR), diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs index d5dfcd10a6683..67ddae45bc07a 100644 --- a/clippy_lints/src/lib.register_complexity.rs +++ b/clippy_lints/src/lib.register_complexity.rs @@ -5,6 +5,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![ LintId::of(attrs::DEPRECATED_CFG_ATTR), LintId::of(booleans::NONMINIMAL_BOOL), + LintId::of(borrow_deref_ref::BORROW_DEREF_REF), LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN), LintId::of(casts::CHAR_LIT_AS_U8), LintId::of(casts::UNNECESSARY_CAST), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 1f2132cf62003..42a17eb12ce52 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -64,6 +64,7 @@ store.register_lints(&[ booleans::LOGIC_BUG, booleans::NONMINIMAL_BOOL, borrow_as_ptr::BORROW_AS_PTR, + borrow_deref_ref::BORROW_DEREF_REF, bytecount::NAIVE_BYTECOUNT, bytes_count_to_len::BYTES_COUNT_TO_LEN, cargo::CARGO_COMMON_METADATA, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d611600cf711b..ca5e62ff431e7 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -183,6 +183,7 @@ mod blocks_in_if_conditions; mod bool_assert_comparison; mod booleans; mod borrow_as_ptr; +mod borrow_deref_ref; mod bytecount; mod bytes_count_to_len; mod cargo; @@ -638,6 +639,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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_borrowed_ref::NeedlessBorrowedRef)); + store.register_late_pass(|| Box::new(borrow_deref_ref::BorrowDerefRef)); 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)); diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 70a118d6b3539..c025f5972d519 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -146,7 +146,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { if arms.is_empty() { e } else { - let arms = never_loop_expr_branch(&mut arms.iter().map(|a| &*a.body), main_loop_id); + let arms = never_loop_expr_branch(&mut arms.iter().map(|a| a.body), main_loop_id); combine_seq(e, arms) } }, diff --git a/clippy_lints/src/matches/match_bool.rs b/clippy_lints/src/matches/match_bool.rs index 90c50b994d2bf..1c216e135704c 100644 --- a/clippy_lints/src/matches/match_bool.rs +++ b/clippy_lints/src/matches/match_bool.rs @@ -24,8 +24,8 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: let exprs = if let PatKind::Lit(arm_bool) = arms[0].pat.kind { if let ExprKind::Lit(ref lit) = arm_bool.kind { match lit.node { - LitKind::Bool(true) => Some((&*arms[0].body, &*arms[1].body)), - LitKind::Bool(false) => Some((&*arms[1].body, &*arms[0].body)), + LitKind::Bool(true) => Some((arms[0].body, arms[1].body)), + LitKind::Bool(false) => Some((arms[1].body, arms[0].body)), _ => None, } } else { diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 97c4feb3122a0..b4c6bfb31ed1c 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -415,7 +415,7 @@ fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id: /// Returns true if the named method can be used to convert the receiver to its "owned" /// representation. fn is_to_owned_like(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool { - is_clone_like(cx, &*method_name.as_str(), method_def_id) + is_clone_like(cx, method_name.as_str(), method_def_id) || is_cow_into_owned(cx, method_name, method_def_id) || is_to_string(cx, method_name, method_def_id) } diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index 405fc23e8de0a..024bd0760715e 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -120,7 +120,7 @@ impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> { self.visit_expr(if_expr); } // make sure top level arm expressions aren't linted - self.maybe_walk_expr(&*arm.body); + self.maybe_walk_expr(arm.body); } }, _ => walk_expr(self, e), diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index cba54e14212d0..7e2531c7ca5f0 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -105,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for MutableKeyType { if let hir::PatKind::Wild = local.pat.kind { return; } - check_ty(cx, local.span, cx.typeck_results().pat_ty(&*local.pat)); + check_ty(cx, local.span, cx.typeck_results().pat_ty(local.pat)); } } diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index b7a56970b3355..21acf003d92b2 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -61,7 +61,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir expr_visitor_no_bodies(|expr| { let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return true }; if matches!( - &*cx.tcx.item_name(macro_call.def_id).as_str(), + cx.tcx.item_name(macro_call.def_id).as_str(), "unimplemented" | "unreachable" | "panic" | "todo" | "assert" | "assert_eq" | "assert_ne" ) { panics.push(macro_call.span); diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index e3ded716341f6..5a93431f25a98 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -233,7 +233,7 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue { } if let hir::TraitItemKind::Fn(method_sig, _) = &item.kind { - self.check_poly_fn(cx, item.def_id, &*method_sig.decl, None); + self.check_poly_fn(cx, item.def_id, method_sig.decl, None); } } diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 0004b8afdd375..f7a6fbac16637 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -288,8 +288,8 @@ fn is_call_with_ref_arg<'tcx>( if let mir::TerminatorKind::Call { func, args, destination, .. } = kind; if args.len() == 1; if let mir::Operand::Move(mir::Place { local, .. }) = &args[0]; - if let ty::FnDef(def_id, _) = *func.ty(&*mir, cx.tcx).kind(); - if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(&*mir, cx.tcx)); + if let ty::FnDef(def_id, _) = *func.ty(mir, cx.tcx).kind(); + if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(mir, cx.tcx)); if !is_copy(cx, inner_ty); then { Some((def_id, *local, inner_ty, destination.as_ref().map(|(dest, _)| dest)?.as_local()?)) @@ -318,7 +318,7 @@ fn find_stmt_assigns_to<'tcx>( None })?; - match (by_ref, &*rvalue) { + match (by_ref, rvalue) { (true, mir::Rvalue::Ref(_, _, place)) | (false, mir::Rvalue::Use(mir::Operand::Copy(place))) => { Some(base_local_and_movability(cx, mir, *place)) }, diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index 2d26c49252fa5..0825f00f421c5 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -51,12 +51,12 @@ impl RedundantStaticLifetimes { fn visit_type(&mut self, ty: &Ty, cx: &EarlyContext<'_>, reason: &str) { match ty.kind { // Be careful of nested structures (arrays and tuples) - TyKind::Array(ref ty, _) => { - self.visit_type(&*ty, cx, reason); + TyKind::Array(ref ty, _) | TyKind::Slice(ref ty) => { + self.visit_type(ty, cx, reason); }, TyKind::Tup(ref tup) => { for tup_ty in tup { - self.visit_type(&*tup_ty, cx, reason); + self.visit_type(tup_ty, cx, reason); } }, // This is what we are looking for ! @@ -89,9 +89,6 @@ impl RedundantStaticLifetimes { } self.visit_type(&*borrow_type.ty, cx, reason); }, - TyKind::Slice(ref ty) => { - self.visit_type(ty, cx, reason); - }, _ => {}, } } diff --git a/clippy_lints/src/size_of_in_element_count.rs b/clippy_lints/src/size_of_in_element_count.rs index 3d7dc49b406a6..bfb9f0d01e1dc 100644 --- a/clippy_lints/src/size_of_in_element_count.rs +++ b/clippy_lints/src/size_of_in_element_count.rs @@ -110,7 +110,7 @@ fn get_pointee_ty_and_count_expr<'tcx>( // Find calls to copy_{from,to}{,_nonoverlapping} and write_bytes methods if let ExprKind::MethodCall(method_path, [ptr_self, .., count], _) = expr.kind; let method_ident = method_path.ident.as_str(); - if METHODS.iter().any(|m| *m == &*method_ident); + if METHODS.iter().any(|m| *m == method_ident); // Get the pointee type if let ty::RawPtr(TypeAndMut { ty: pointee_ty, .. }) = diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 0e8f40e92101a..60f9887699498 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -422,7 +422,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { } } else if let Some(macro_call) = root_macro_call_first_node(cx, item) { if !matches!( - &*cx.tcx.item_name(macro_call.def_id).as_str(), + cx.tcx.item_name(macro_call.def_id).as_str(), "impl_lint_pass" | "declare_lint_pass" ) { return; @@ -504,7 +504,7 @@ fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<' return; } - if RustcVersion::parse(&*value.as_str()).is_err() { + if RustcVersion::parse(value.as_str()).is_err() { span_lint_and_help( cx, INVALID_CLIPPY_VERSION_ATTRIBUTE, @@ -595,7 +595,7 @@ impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions { if_chain! { if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind; let fn_name = path.ident; - if let Some(sugg) = self.map.get(&*fn_name.as_str()); + if let Some(sugg) = self.map.get(fn_name.as_str()); let ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); if match_type(cx, ty, &paths::EARLY_CONTEXT) || match_type(cx, ty, &paths::LATE_CONTEXT); @@ -679,7 +679,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { then { let and_then_snippets = get_and_then_snippets(cx, and_then_args); let mut sle = SpanlessEq::new(cx).deny_side_effects(); - match &*ps.ident.as_str() { + match ps.ident.as_str() { "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => { suggest_suggestion(cx, expr, &and_then_snippets, &span_suggestion_snippets(cx, span_call_args)); }, diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 8c1910b3b2af8..53579ccb4c2ba 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -527,12 +527,11 @@ fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option, item: &Item<'_>) -> Option { let attrs = cx.tcx.hir().attrs(item.hir_id()); let mut lines = attrs.iter().filter_map(ast::Attribute::doc_str); - let mut docs = String::from(&*lines.next()?.as_str()); + let mut docs = String::from(lines.next()?.as_str()); let mut in_code_block = false; let mut is_code_block_rust = false; for line in lines { let line = line.as_str(); - let line = &*line; // Rustdoc hides code lines starting with `# ` and this removes them from Clippy's lint list :) if is_code_block_rust && line.trim_start().starts_with("# ") { diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 9f162a117b2d1..d487868cafe50 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -478,7 +478,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { fn ifthenelse(&mut self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option { if let Some(Constant::Bool(b)) = self.expr(cond) { if b { - self.expr(&*then) + self.expr(then) } else { otherwise.as_ref().and_then(|expr| self.expr(expr)) } diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 1e0fc789af243..4604ae5c2c7f0 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -35,7 +35,7 @@ impl<'tcx> ForLoop<'tcx> { if let hir::ExprKind::Match(iterexpr, [arm], hir::MatchSource::ForLoopDesugar) = e.kind; if let hir::ExprKind::Call(_, [arg]) = iterexpr.kind; if let hir::ExprKind::Loop(block, ..) = arm.body.kind; - if let [stmt] = &*block.stmts; + if let [stmt] = block.stmts; if let hir::StmtKind::Expr(e) = stmt.kind; if let hir::ExprKind::Match(_, [_, some_arm], _) = e.kind; if let hir::PatKind::Struct(_, [field], _) = some_arm.pat.kind; diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index c440793b90e0e..cbd0a257cfb62 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -411,10 +411,10 @@ impl HirEqInterExpr<'_, '_, '_> { (&TyKind::Slice(l_vec), &TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec), (&TyKind::Array(lt, ll), &TyKind::Array(rt, rl)) => self.eq_ty(lt, rt) && self.eq_array_length(ll, rl), (&TyKind::Ptr(ref l_mut), &TyKind::Ptr(ref r_mut)) => { - l_mut.mutbl == r_mut.mutbl && self.eq_ty(&*l_mut.ty, &*r_mut.ty) + l_mut.mutbl == r_mut.mutbl && self.eq_ty(l_mut.ty, r_mut.ty) }, (&TyKind::Rptr(_, ref l_rmut), &TyKind::Rptr(_, ref r_rmut)) => { - l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(&*l_rmut.ty, &*r_rmut.ty) + l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(l_rmut.ty, r_rmut.ty) }, (&TyKind::Path(ref l), &TyKind::Path(ref r)) => self.eq_qpath(l, r), (&TyKind::Tup(l), &TyKind::Tup(r)) => over(l, r, |l, r| self.eq_ty(l, r)), @@ -608,7 +608,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_name(i.ident.name); } if let Some(j) = *j { - self.hash_expr(&*j); + self.hash_expr(j); } }, ExprKind::Box(e) | ExprKind::DropTemps(e) | ExprKind::Yield(e, _) => { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index b8a80d4adc31b..1ca7cf5c17743 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1443,7 +1443,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { }, PatKind::Tuple(pats, _) => are_refutable(cx, pats), PatKind::Struct(ref qpath, fields, _) => { - is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| &*field.pat)) + is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat)) }, PatKind::TupleStruct(ref qpath, pats, _) => is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats), PatKind::Slice(head, middle, tail) => { @@ -1658,7 +1658,7 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, let mut blocks: Vec<&Block<'_>> = Vec::new(); while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) { - conds.push(&*cond); + conds.push(cond); if let ExprKind::Block(block, _) = then.kind { blocks.push(block); } else { diff --git a/tests/ui-internal/unnecessary_symbol_str.fixed b/tests/ui-internal/unnecessary_symbol_str.fixed index 4f5336663a8f7..6033d06e4f637 100644 --- a/tests/ui-internal/unnecessary_symbol_str.fixed +++ b/tests/ui-internal/unnecessary_symbol_str.fixed @@ -2,6 +2,7 @@ #![feature(rustc_private)] #![deny(clippy::internal)] #![allow( + clippy::borrow_deref_ref, clippy::unnecessary_operation, unused_must_use, clippy::missing_clippy_version_attribute diff --git a/tests/ui-internal/unnecessary_symbol_str.rs b/tests/ui-internal/unnecessary_symbol_str.rs index 894aa1d3bc6ea..1bb5d55f0b60c 100644 --- a/tests/ui-internal/unnecessary_symbol_str.rs +++ b/tests/ui-internal/unnecessary_symbol_str.rs @@ -2,6 +2,7 @@ #![feature(rustc_private)] #![deny(clippy::internal)] #![allow( + clippy::borrow_deref_ref, clippy::unnecessary_operation, unused_must_use, clippy::missing_clippy_version_attribute diff --git a/tests/ui-internal/unnecessary_symbol_str.stderr b/tests/ui-internal/unnecessary_symbol_str.stderr index 75367bf4bc519..a1f507f331d26 100644 --- a/tests/ui-internal/unnecessary_symbol_str.stderr +++ b/tests/ui-internal/unnecessary_symbol_str.stderr @@ -1,5 +1,5 @@ error: unnecessary `Symbol` to string conversion - --> $DIR/unnecessary_symbol_str.rs:15:5 + --> $DIR/unnecessary_symbol_str.rs:16:5 | LL | Symbol::intern("foo").as_str() == "clippy"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::sym::clippy` @@ -12,25 +12,25 @@ LL | #![deny(clippy::internal)] = note: `#[deny(clippy::unnecessary_symbol_str)]` implied by `#[deny(clippy::internal)]` error: unnecessary `Symbol` to string conversion - --> $DIR/unnecessary_symbol_str.rs:16:5 + --> $DIR/unnecessary_symbol_str.rs:17:5 | LL | Symbol::intern("foo").to_string() == "self"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::symbol::kw::SelfLower` error: unnecessary `Symbol` to string conversion - --> $DIR/unnecessary_symbol_str.rs:17:5 + --> $DIR/unnecessary_symbol_str.rs:18:5 | LL | Symbol::intern("foo").to_ident_string() != "Self"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") != rustc_span::symbol::kw::SelfUpper` error: unnecessary `Symbol` to string conversion - --> $DIR/unnecessary_symbol_str.rs:18:5 + --> $DIR/unnecessary_symbol_str.rs:19:5 | LL | &*Ident::empty().as_str() == "clippy"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Ident::empty().name == rustc_span::sym::clippy` error: unnecessary `Symbol` to string conversion - --> $DIR/unnecessary_symbol_str.rs:19:5 + --> $DIR/unnecessary_symbol_str.rs:20:5 | LL | "clippy" == Ident::empty().to_string(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::clippy == Ident::empty().name` diff --git a/tests/ui/borrow_deref_ref.rs b/tests/ui/borrow_deref_ref.rs new file mode 100644 index 0000000000000..c8a2c397ff6d3 --- /dev/null +++ b/tests/ui/borrow_deref_ref.rs @@ -0,0 +1,58 @@ +fn main() {} + +mod should_lint { + fn foo() { + let a = &12; + let b = &*a; + + let s = &String::new(); + let x: &str = &*s; + + let b = &mut &*bar(&12); + } + + fn bar(x: &u32) -> &u32 { + x + } +} + +// this mod explains why we should not lint `&mut &* (&T)` +mod should_not_lint1 { + fn foo(x: &mut &u32) { + *x = &1; + } + + fn main() { + let mut x = &0; + foo(&mut &*x); // should not lint + assert_eq!(*x, 0); + + foo(&mut x); + assert_eq!(*x, 1); + } +} + +// similar to should_not_lint1 +mod should_not_lint2 { + struct S<'a> { + a: &'a u32, + b: u32, + } + + fn main() { + let s = S { a: &1, b: 1 }; + let x = &mut &*s.a; + *x = &2; + } +} + +// this mod explains why we should not lint `& &* (&T)` +mod false_negative { + fn foo() { + let x = &12; + let addr_x = &x as *const _ as usize; + let addr_y = &&*x as *const _ as usize; // assert ok + // let addr_y = &x as *const _ as usize; // assert fail + assert_ne!(addr_x, addr_y); + } +} diff --git a/tests/ui/borrow_deref_ref.stderr b/tests/ui/borrow_deref_ref.stderr new file mode 100644 index 0000000000000..b550b9285199d --- /dev/null +++ b/tests/ui/borrow_deref_ref.stderr @@ -0,0 +1,37 @@ +error: deref on an immutable reference + --> $DIR/borrow_deref_ref.rs:6:17 + | +LL | let b = &*a; + | ^^^ help: if you would like to reborrow, try removing `&*`: `a` + | + = note: `-D clippy::borrow-deref-ref` implied by `-D warnings` + +error: deref on an immutable reference + --> $DIR/borrow_deref_ref.rs:9:23 + | +LL | let x: &str = &*s; + | ^^^ + | +help: if you would like to reborrow, try removing `&*` + | +LL | let x: &str = s; + | ~ +help: if you would like to deref, try using `&**` + | +LL | let x: &str = &**s; + | ~~~~ + +error: deref on an immutable reference + --> $DIR/borrow_deref_ref.rs:11:22 + | +LL | let b = &mut &*bar(&12); + | ^^^^^^^^^^ help: if you would like to reborrow, try removing `&*`: `bar(&12)` + +error: deref on an immutable reference + --> $DIR/borrow_deref_ref.rs:54:23 + | +LL | let addr_y = &&*x as *const _ as usize; // assert ok + | ^^^ help: if you would like to reborrow, try removing `&*`: `x` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/collapsible_match2.rs b/tests/ui/collapsible_match2.rs index 542e69484276d..c8fb0a39e954c 100644 --- a/tests/ui/collapsible_match2.rs +++ b/tests/ui/collapsible_match2.rs @@ -57,7 +57,7 @@ fn lint_cases(opt_opt: Option>, res_opt: Result, String> // ref pattern and deref match Some(&[1]) { - Some(ref s) => match &*s { + Some(ref s) => match s { [n] => foo(n), _ => (), }, diff --git a/tests/ui/collapsible_match2.stderr b/tests/ui/collapsible_match2.stderr index 46b645aea135c..fe64e4693792d 100644 --- a/tests/ui/collapsible_match2.stderr +++ b/tests/ui/collapsible_match2.stderr @@ -78,7 +78,7 @@ LL | [n] => foo(n), error: this `match` can be collapsed into the outer `match` --> $DIR/collapsible_match2.rs:60:24 | -LL | Some(ref s) => match &*s { +LL | Some(ref s) => match s { | ________________________^ LL | | [n] => foo(n), LL | | _ => (), @@ -88,7 +88,7 @@ LL | | }, help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match2.rs:60:14 | -LL | Some(ref s) => match &*s { +LL | Some(ref s) => match s { | ^^^^^ replace this binding LL | [n] => foo(n), | ^^^ with this pattern diff --git a/tests/ui/deref_by_slicing.fixed b/tests/ui/deref_by_slicing.fixed index b26276218b78c..257393e56ff0f 100644 --- a/tests/ui/deref_by_slicing.fixed +++ b/tests/ui/deref_by_slicing.fixed @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::deref_by_slicing)] +#![allow(clippy::borrow_deref_ref)] use std::io::Read; diff --git a/tests/ui/deref_by_slicing.rs b/tests/ui/deref_by_slicing.rs index 6aa1408ba1769..e288046f927fd 100644 --- a/tests/ui/deref_by_slicing.rs +++ b/tests/ui/deref_by_slicing.rs @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::deref_by_slicing)] +#![allow(clippy::borrow_deref_ref)] use std::io::Read; diff --git a/tests/ui/deref_by_slicing.stderr b/tests/ui/deref_by_slicing.stderr index ffd76de378df1..8f042ef47ebe3 100644 --- a/tests/ui/deref_by_slicing.stderr +++ b/tests/ui/deref_by_slicing.stderr @@ -1,5 +1,5 @@ error: slicing when dereferencing would work - --> $DIR/deref_by_slicing.rs:9:13 + --> $DIR/deref_by_slicing.rs:10:13 | LL | let _ = &vec[..]; | ^^^^^^^^ help: dereference the original value instead: `&*vec` @@ -7,49 +7,49 @@ LL | let _ = &vec[..]; = note: `-D clippy::deref-by-slicing` implied by `-D warnings` error: slicing when dereferencing would work - --> $DIR/deref_by_slicing.rs:10:13 + --> $DIR/deref_by_slicing.rs:11:13 | LL | let _ = &mut vec[..]; | ^^^^^^^^^^^^ help: dereference the original value instead: `&mut *vec` error: slicing when dereferencing would work - --> $DIR/deref_by_slicing.rs:13:13 + --> $DIR/deref_by_slicing.rs:14:13 | LL | let _ = &ref_vec[..]; | ^^^^^^^^^^^^ help: dereference the original value instead: `&**ref_vec` error: slicing when dereferencing would work - --> $DIR/deref_by_slicing.rs:14:21 + --> $DIR/deref_by_slicing.rs:15:21 | LL | let mut_slice = &mut ref_vec[..]; | ^^^^^^^^^^^^^^^^ help: dereference the original value instead: `&mut **ref_vec` error: slicing when dereferencing would work - --> $DIR/deref_by_slicing.rs:15:13 + --> $DIR/deref_by_slicing.rs:16:13 | LL | let _ = &mut mut_slice[..]; // Err, re-borrows slice | ^^^^^^^^^^^^^^^^^^ help: reborrow the original value instead: `&mut *mut_slice` error: slicing when dereferencing would work - --> $DIR/deref_by_slicing.rs:18:13 + --> $DIR/deref_by_slicing.rs:19:13 | LL | let _ = &s[..]; | ^^^^^^ help: dereference the original value instead: `&*s` error: slicing when dereferencing would work - --> $DIR/deref_by_slicing.rs:21:18 + --> $DIR/deref_by_slicing.rs:22:18 | LL | let _ = &mut &S[..]; // Err, re-borrows slice | ^^^^^^ help: reborrow the original value instead: `&*S` error: slicing when dereferencing would work - --> $DIR/deref_by_slicing.rs:25:13 + --> $DIR/deref_by_slicing.rs:26:13 | LL | let _ = &slice_ref[..]; // Err, derefs slice | ^^^^^^^^^^^^^^ help: dereference the original value instead: `*slice_ref` error: slicing when dereferencing would work - --> $DIR/deref_by_slicing.rs:28:13 + --> $DIR/deref_by_slicing.rs:29:13 | LL | let _ = (&bytes[..]).read_to_end(&mut vec![]).unwrap(); // Err, re-borrows slice | ^^^^^^^^^^^^ help: reborrow the original value instead: `(&*bytes)` diff --git a/tests/ui/explicit_deref_methods.fixed b/tests/ui/explicit_deref_methods.fixed index 3de2a51ffa5f3..92f27e68549a3 100644 --- a/tests/ui/explicit_deref_methods.fixed +++ b/tests/ui/explicit_deref_methods.fixed @@ -1,6 +1,11 @@ // run-rustfix -#![allow(unused_variables, clippy::clone_double_ref, clippy::needless_borrow)] +#![allow( + unused_variables, + clippy::clone_double_ref, + clippy::needless_borrow, + clippy::borrow_deref_ref +)] #![warn(clippy::explicit_deref_methods)] use std::ops::{Deref, DerefMut}; diff --git a/tests/ui/explicit_deref_methods.rs b/tests/ui/explicit_deref_methods.rs index a08d75964220a..d118607f992b9 100644 --- a/tests/ui/explicit_deref_methods.rs +++ b/tests/ui/explicit_deref_methods.rs @@ -1,6 +1,11 @@ // run-rustfix -#![allow(unused_variables, clippy::clone_double_ref, clippy::needless_borrow)] +#![allow( + unused_variables, + clippy::clone_double_ref, + clippy::needless_borrow, + clippy::borrow_deref_ref +)] #![warn(clippy::explicit_deref_methods)] use std::ops::{Deref, DerefMut}; diff --git a/tests/ui/explicit_deref_methods.stderr b/tests/ui/explicit_deref_methods.stderr index 8035d77d18d5c..8e8b358972be7 100644 --- a/tests/ui/explicit_deref_methods.stderr +++ b/tests/ui/explicit_deref_methods.stderr @@ -1,5 +1,5 @@ error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:30:19 + --> $DIR/explicit_deref_methods.rs:35:19 | LL | let b: &str = a.deref(); | ^^^^^^^^^ help: try this: `&*a` @@ -7,67 +7,67 @@ LL | let b: &str = a.deref(); = note: `-D clippy::explicit-deref-methods` implied by `-D warnings` error: explicit `deref_mut` method call - --> $DIR/explicit_deref_methods.rs:32:23 + --> $DIR/explicit_deref_methods.rs:37:23 | LL | let b: &mut str = a.deref_mut(); | ^^^^^^^^^^^^^ help: try this: `&mut **a` error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:35:39 + --> $DIR/explicit_deref_methods.rs:40:39 | LL | let b: String = format!("{}, {}", a.deref(), a.deref()); | ^^^^^^^^^ help: try this: `&*a` error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:35:50 + --> $DIR/explicit_deref_methods.rs:40:50 | LL | let b: String = format!("{}, {}", a.deref(), a.deref()); | ^^^^^^^^^ help: try this: `&*a` error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:37:20 + --> $DIR/explicit_deref_methods.rs:42:20 | LL | println!("{}", a.deref()); | ^^^^^^^^^ help: try this: `&*a` error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:40:11 + --> $DIR/explicit_deref_methods.rs:45:11 | LL | match a.deref() { | ^^^^^^^^^ help: try this: `&*a` error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:44:28 + --> $DIR/explicit_deref_methods.rs:49:28 | LL | let b: String = concat(a.deref()); | ^^^^^^^^^ help: try this: `&*a` error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:46:13 + --> $DIR/explicit_deref_methods.rs:51:13 | LL | let b = just_return(a).deref(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `just_return(a)` error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:48:28 + --> $DIR/explicit_deref_methods.rs:53:28 | LL | let b: String = concat(just_return(a).deref()); | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `just_return(a)` error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:50:19 + --> $DIR/explicit_deref_methods.rs:55:19 | LL | let b: &str = a.deref().deref(); | ^^^^^^^^^^^^^^^^^ help: try this: `&**a` error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:53:13 + --> $DIR/explicit_deref_methods.rs:58:13 | LL | let b = opt_a.unwrap().deref(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*opt_a.unwrap()` error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:79:31 + --> $DIR/explicit_deref_methods.rs:84:31 | LL | let b: &str = expr_deref!(a.deref()); | ^^^^^^^^^ help: try this: `&*a` diff --git a/tests/ui/forget_ref.rs b/tests/ui/forget_ref.rs index 6c8c4c9c0edec..031b415f56ff6 100644 --- a/tests/ui/forget_ref.rs +++ b/tests/ui/forget_ref.rs @@ -1,6 +1,7 @@ #![warn(clippy::forget_ref)] #![allow(clippy::toplevel_ref_arg)] #![allow(clippy::unnecessary_wraps, clippy::forget_non_drop)] +#![allow(clippy::borrow_deref_ref)] use std::mem::forget; diff --git a/tests/ui/forget_ref.stderr b/tests/ui/forget_ref.stderr index 73409388ed16a..df5cd8cacdb8d 100644 --- a/tests/ui/forget_ref.stderr +++ b/tests/ui/forget_ref.stderr @@ -1,108 +1,108 @@ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing - --> $DIR/forget_ref.rs:10:5 + --> $DIR/forget_ref.rs:11:5 | LL | forget(&SomeStruct); | ^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::forget-ref` implied by `-D warnings` note: argument has type `&SomeStruct` - --> $DIR/forget_ref.rs:10:12 + --> $DIR/forget_ref.rs:11:12 | LL | forget(&SomeStruct); | ^^^^^^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing - --> $DIR/forget_ref.rs:13:5 + --> $DIR/forget_ref.rs:14:5 | LL | forget(&owned); | ^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/forget_ref.rs:13:12 + --> $DIR/forget_ref.rs:14:12 | LL | forget(&owned); | ^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing - --> $DIR/forget_ref.rs:14:5 + --> $DIR/forget_ref.rs:15:5 | LL | forget(&&owned); | ^^^^^^^^^^^^^^^ | note: argument has type `&&SomeStruct` - --> $DIR/forget_ref.rs:14:12 + --> $DIR/forget_ref.rs:15:12 | LL | forget(&&owned); | ^^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing - --> $DIR/forget_ref.rs:15:5 + --> $DIR/forget_ref.rs:16:5 | LL | forget(&mut owned); | ^^^^^^^^^^^^^^^^^^ | note: argument has type `&mut SomeStruct` - --> $DIR/forget_ref.rs:15:12 + --> $DIR/forget_ref.rs:16:12 | LL | forget(&mut owned); | ^^^^^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing - --> $DIR/forget_ref.rs:19:5 + --> $DIR/forget_ref.rs:20:5 | LL | forget(&*reference1); | ^^^^^^^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/forget_ref.rs:19:12 + --> $DIR/forget_ref.rs:20:12 | LL | forget(&*reference1); | ^^^^^^^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing - --> $DIR/forget_ref.rs:22:5 + --> $DIR/forget_ref.rs:23:5 | LL | forget(reference2); | ^^^^^^^^^^^^^^^^^^ | note: argument has type `&mut SomeStruct` - --> $DIR/forget_ref.rs:22:12 + --> $DIR/forget_ref.rs:23:12 | LL | forget(reference2); | ^^^^^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing - --> $DIR/forget_ref.rs:25:5 + --> $DIR/forget_ref.rs:26:5 | LL | forget(reference3); | ^^^^^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/forget_ref.rs:25:12 + --> $DIR/forget_ref.rs:26:12 | LL | forget(reference3); | ^^^^^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing - --> $DIR/forget_ref.rs:30:5 + --> $DIR/forget_ref.rs:31:5 | LL | forget(&val); | ^^^^^^^^^^^^ | note: argument has type `&T` - --> $DIR/forget_ref.rs:30:12 + --> $DIR/forget_ref.rs:31:12 | LL | forget(&val); | ^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing - --> $DIR/forget_ref.rs:38:5 + --> $DIR/forget_ref.rs:39:5 | LL | std::mem::forget(&SomeStruct); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/forget_ref.rs:38:22 + --> $DIR/forget_ref.rs:39:22 | LL | std::mem::forget(&SomeStruct); | ^^^^^^^^^^^ diff --git a/tests/ui/recursive_format_impl.rs b/tests/ui/recursive_format_impl.rs index f72fc77ab9977..cb6ba36b14c80 100644 --- a/tests/ui/recursive_format_impl.rs +++ b/tests/ui/recursive_format_impl.rs @@ -2,7 +2,8 @@ #![allow( clippy::inherent_to_string_shadow_display, clippy::to_string_in_format_args, - clippy::deref_addrof + clippy::deref_addrof, + clippy::borrow_deref_ref )] use std::fmt; diff --git a/tests/ui/recursive_format_impl.stderr b/tests/ui/recursive_format_impl.stderr index 1a717ac92d8b7..84ce69df56696 100644 --- a/tests/ui/recursive_format_impl.stderr +++ b/tests/ui/recursive_format_impl.stderr @@ -1,5 +1,5 @@ error: using `self.to_string` in `fmt::Display` implementation will cause infinite recursion - --> $DIR/recursive_format_impl.rs:29:25 + --> $DIR/recursive_format_impl.rs:30:25 | LL | write!(f, "{}", self.to_string()) | ^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | write!(f, "{}", self.to_string()) = note: `-D clippy::recursive-format-impl` implied by `-D warnings` error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:73:9 + --> $DIR/recursive_format_impl.rs:74:9 | LL | write!(f, "{}", self) | ^^^^^^^^^^^^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | write!(f, "{}", self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:82:9 + --> $DIR/recursive_format_impl.rs:83:9 | LL | write!(f, "{}", &self) | ^^^^^^^^^^^^^^^^^^^^^^ @@ -23,7 +23,7 @@ LL | write!(f, "{}", &self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Debug` in `impl Debug` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:88:9 + --> $DIR/recursive_format_impl.rs:89:9 | LL | write!(f, "{:?}", &self) | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -31,7 +31,7 @@ LL | write!(f, "{:?}", &self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:97:9 + --> $DIR/recursive_format_impl.rs:98:9 | LL | write!(f, "{}", &&&self) | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -39,7 +39,7 @@ LL | write!(f, "{}", &&&self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:171:9 + --> $DIR/recursive_format_impl.rs:172:9 | LL | write!(f, "{}", &*self) | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -47,7 +47,7 @@ LL | write!(f, "{}", &*self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Debug` in `impl Debug` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:177:9 + --> $DIR/recursive_format_impl.rs:178:9 | LL | write!(f, "{:?}", &*self) | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -55,7 +55,7 @@ LL | write!(f, "{:?}", &*self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:193:9 + --> $DIR/recursive_format_impl.rs:194:9 | LL | write!(f, "{}", *self) | ^^^^^^^^^^^^^^^^^^^^^^ @@ -63,7 +63,7 @@ LL | write!(f, "{}", *self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:209:9 + --> $DIR/recursive_format_impl.rs:210:9 | LL | write!(f, "{}", **&&*self) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -71,7 +71,7 @@ LL | write!(f, "{}", **&&*self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:225:9 + --> $DIR/recursive_format_impl.rs:226:9 | LL | write!(f, "{}", &&**&&*self) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 8430fa2a82712ac137b2bc0215301e2c189d4a2e Mon Sep 17 00:00:00 2001 From: lengyijun Date: Mon, 30 May 2022 01:49:46 +0000 Subject: [PATCH 49/79] run-rustfix --- tests/ui/borrow_deref_ref.rs | 4 ++++ tests/ui/borrow_deref_ref.stderr | 8 ++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/ui/borrow_deref_ref.rs b/tests/ui/borrow_deref_ref.rs index c8a2c397ff6d3..cf502775b56cc 100644 --- a/tests/ui/borrow_deref_ref.rs +++ b/tests/ui/borrow_deref_ref.rs @@ -1,3 +1,7 @@ +// run-rustfix + +#![allow(dead_code, unused_variables)] + fn main() {} mod should_lint { diff --git a/tests/ui/borrow_deref_ref.stderr b/tests/ui/borrow_deref_ref.stderr index b550b9285199d..a34dbcc2e8afd 100644 --- a/tests/ui/borrow_deref_ref.stderr +++ b/tests/ui/borrow_deref_ref.stderr @@ -1,5 +1,5 @@ error: deref on an immutable reference - --> $DIR/borrow_deref_ref.rs:6:17 + --> $DIR/borrow_deref_ref.rs:10:17 | LL | let b = &*a; | ^^^ help: if you would like to reborrow, try removing `&*`: `a` @@ -7,7 +7,7 @@ LL | let b = &*a; = note: `-D clippy::borrow-deref-ref` implied by `-D warnings` error: deref on an immutable reference - --> $DIR/borrow_deref_ref.rs:9:23 + --> $DIR/borrow_deref_ref.rs:13:23 | LL | let x: &str = &*s; | ^^^ @@ -22,13 +22,13 @@ LL | let x: &str = &**s; | ~~~~ error: deref on an immutable reference - --> $DIR/borrow_deref_ref.rs:11:22 + --> $DIR/borrow_deref_ref.rs:15:22 | LL | let b = &mut &*bar(&12); | ^^^^^^^^^^ help: if you would like to reborrow, try removing `&*`: `bar(&12)` error: deref on an immutable reference - --> $DIR/borrow_deref_ref.rs:54:23 + --> $DIR/borrow_deref_ref.rs:58:23 | LL | let addr_y = &&*x as *const _ as usize; // assert ok | ^^^ help: if you would like to reborrow, try removing `&*`: `x` From 2aa45693039c2a5c235125e065c68c5e155fbaf1 Mon Sep 17 00:00:00 2001 From: Micha White Date: Thu, 26 May 2022 12:52:43 -0400 Subject: [PATCH 50/79] Triggered the lint on tuple struct and struct patterns --- clippy_lints/src/use_self.rs | 18 +++++++--- tests/ui/use_self.fixed | 66 ++++++++++++++++++++++++++++++++++++ tests/ui/use_self.rs | 66 ++++++++++++++++++++++++++++++++++++ tests/ui/use_self.stderr | 62 ++++++++++++++++++++++++++++++++- 4 files changed, 206 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 66f7748e9e089..486ea5e5ccfa9 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -258,13 +258,21 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { if !pat.span.from_expansion(); if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS); if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last(); - if let PatKind::Path(QPath::Resolved(_, path)) = pat.kind; - if !matches!(path.res, Res::SelfTy { .. } | Res::Def(DefKind::TyParam, _)); + // get the path from the pattern + if let PatKind::Path(QPath::Resolved(_, path)) + | PatKind::TupleStruct(QPath::Resolved(_, path), _, _) + | PatKind::Struct(QPath::Resolved(_, path), _, _) = pat.kind; if cx.typeck_results().pat_ty(pat) == cx.tcx.type_of(impl_id); - if let [first, ..] = path.segments; - if let Some(hir_id) = first.hir_id; then { - span_lint(cx, cx.tcx.hir().span(hir_id)); + match path.res { + Res::Def(DefKind::Ctor(ctor_of, _), ..) => match ctor_of { + CtorOf::Variant => lint_path_to_variant(cx, path), + CtorOf::Struct => span_lint(cx, path.span), + }, + Res::Def(DefKind::Variant, ..) => lint_path_to_variant(cx, path), + Res::Def(DefKind::Struct, ..) => span_lint(cx, path.span), + _ => () + } } } } diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index 3e62ffe74fedd..4f80aaecc902d 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -542,3 +542,69 @@ mod use_self_in_pat { } } } + +mod issue8845 { + pub enum Something { + Num(u8), + TupleNums(u8, u8), + StructNums { one: u8, two: u8 }, + } + + struct Foo(u8); + + struct Bar { + x: u8, + y: usize, + } + + impl Something { + fn get_value(&self) -> u8 { + match self { + Self::Num(n) => *n, + Self::TupleNums(n, _m) => *n, + Self::StructNums { one, two: _ } => *one, + } + } + + fn use_crate(&self) -> u8 { + match self { + Self::Num(n) => *n, + Self::TupleNums(n, _m) => *n, + Self::StructNums { one, two: _ } => *one, + } + } + + fn imported_values(&self) -> u8 { + use Something::*; + match self { + Num(n) => *n, + TupleNums(n, _m) => *n, + StructNums { one, two: _ } => *one, + } + } + } + + impl Foo { + fn get_value(&self) -> u8 { + let Self(x) = self; + *x + } + + fn use_crate(&self) -> u8 { + let Self(x) = self; + *x + } + } + + impl Bar { + fn get_value(&self) -> u8 { + let Self { x, .. } = self; + *x + } + + fn use_crate(&self) -> u8 { + let Self { x, .. } = self; + *x + } + } +} diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index da2faddee12a7..52da72db53ce3 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -542,3 +542,69 @@ mod use_self_in_pat { } } } + +mod issue8845 { + pub enum Something { + Num(u8), + TupleNums(u8, u8), + StructNums { one: u8, two: u8 }, + } + + struct Foo(u8); + + struct Bar { + x: u8, + y: usize, + } + + impl Something { + fn get_value(&self) -> u8 { + match self { + Something::Num(n) => *n, + Something::TupleNums(n, _m) => *n, + Something::StructNums { one, two: _ } => *one, + } + } + + fn use_crate(&self) -> u8 { + match self { + crate::issue8845::Something::Num(n) => *n, + crate::issue8845::Something::TupleNums(n, _m) => *n, + crate::issue8845::Something::StructNums { one, two: _ } => *one, + } + } + + fn imported_values(&self) -> u8 { + use Something::*; + match self { + Num(n) => *n, + TupleNums(n, _m) => *n, + StructNums { one, two: _ } => *one, + } + } + } + + impl Foo { + fn get_value(&self) -> u8 { + let Foo(x) = self; + *x + } + + fn use_crate(&self) -> u8 { + let crate::issue8845::Foo(x) = self; + *x + } + } + + impl Bar { + fn get_value(&self) -> u8 { + let Bar { x, .. } = self; + *x + } + + fn use_crate(&self) -> u8 { + let crate::issue8845::Bar { x, .. } = self; + *x + } + } +} diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index 34d98618253a6..f06bb959b3bde 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -186,5 +186,65 @@ error: unnecessary structure name repetition LL | if let Foo::Bar = self { | ^^^ help: use the applicable keyword: `Self` -error: aborting due to 31 previous errors +error: unnecessary structure name repetition + --> $DIR/use_self.rs:563:17 + | +LL | Something::Num(n) => *n, + | ^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:564:17 + | +LL | Something::TupleNums(n, _m) => *n, + | ^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:565:17 + | +LL | Something::StructNums { one, two: _ } => *one, + | ^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:571:17 + | +LL | crate::issue8845::Something::Num(n) => *n, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:572:17 + | +LL | crate::issue8845::Something::TupleNums(n, _m) => *n, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:573:17 + | +LL | crate::issue8845::Something::StructNums { one, two: _ } => *one, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:589:17 + | +LL | let Foo(x) = self; + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:594:17 + | +LL | let crate::issue8845::Foo(x) = self; + | ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:601:17 + | +LL | let Bar { x, .. } = self; + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:606:17 + | +LL | let crate::issue8845::Bar { x, .. } = self; + | ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: aborting due to 41 previous errors From 0a7f19b547032eea7c923f6c49f5addc14717c00 Mon Sep 17 00:00:00 2001 From: InfRandomness <43730933+InfRandomness@users.noreply.github.com> Date: Sun, 29 May 2022 20:00:11 +0000 Subject: [PATCH 51/79] Fix #8748 --- clippy_lints/src/shadow.rs | 10 +++++++--- tests/ui/shadow.rs | 7 +++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 1ab7f52110ce7..4f74c1e44c26d 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -160,9 +160,13 @@ impl<'tcx> LateLintPass<'tcx> for Shadow { fn is_shadow(cx: &LateContext<'_>, owner: LocalDefId, first: ItemLocalId, second: ItemLocalId) -> bool { let scope_tree = cx.tcx.region_scope_tree(owner.to_def_id()); - let first_scope = scope_tree.var_scope(first).unwrap(); - let second_scope = scope_tree.var_scope(second).unwrap(); - scope_tree.is_subscope_of(second_scope, first_scope) + if let Some(first_scope) = scope_tree.var_scope(first) { + if let Some(second_scope) = scope_tree.var_scope(second) { + return scope_tree.is_subscope_of(second_scope, first_scope); + } + } + + false } fn lint_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, shadowed: HirId, span: Span) { diff --git a/tests/ui/shadow.rs b/tests/ui/shadow.rs index a394ef8f25c67..1fa9fc749a96a 100644 --- a/tests/ui/shadow.rs +++ b/tests/ui/shadow.rs @@ -88,4 +88,11 @@ pub async fn foo2(_a: i32, _b: i64) { let _b = _a; } +fn ice_8748() { + let _ = [0; { + let x = 1; + if let Some(x) = Some(1) { x } else { 1 } + }]; +} + fn main() {} From 4e45960abcad08dd7803dd44ce1abe45d3958365 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Tue, 31 May 2022 00:00:00 +0000 Subject: [PATCH 52/79] Add a pointer to address cast kind A pointer to address cast are often special-cased. Introduce a dedicated cast kind to make them easy distinguishable. --- clippy_utils/src/qualify_min_const_fn.rs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 78d8f1e213af0..283b20fc24d82 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -125,16 +125,11 @@ fn check_rvalue<'tcx>( Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => { check_place(tcx, *place, span, body) }, - Rvalue::Cast(CastKind::Misc, operand, cast_ty) => { - use rustc_middle::ty::cast::CastTy; - let cast_in = CastTy::from_ty(operand.ty(body, tcx)).expect("bad input type for cast"); - let cast_out = CastTy::from_ty(*cast_ty).expect("bad output type for cast"); - match (cast_in, cast_out) { - (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => { - Err((span, "casting pointers to ints is unstable in const fn".into())) - }, - _ => check_operand(tcx, operand, span, body), - } + Rvalue::Cast(CastKind::PointerAddress, _, _) => { + Err((span, "casting pointers to ints is unstable in const fn".into())) + }, + Rvalue::Cast(CastKind::Misc, operand, _) => { + check_operand(tcx, operand, span, body) }, Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), operand, _) => { check_operand(tcx, operand, span, body) From eb2908b4ea8fd63623f994780d2928f32af84d71 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 30 May 2022 22:07:49 -0400 Subject: [PATCH 53/79] Add lint `almost_complete_letter_range` --- CHANGELOG.md | 1 + .../src/almost_complete_letter_range.rs | 100 ++++++++++++++++++ clippy_lints/src/lib.register_all.rs | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_suspicious.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_utils/src/msrvs.rs | 1 + clippy_utils/src/source.rs | 23 +++- tests/ui/almost_complete_letter_range.fixed | 66 ++++++++++++ tests/ui/almost_complete_letter_range.rs | 66 ++++++++++++ tests/ui/almost_complete_letter_range.stderr | 100 ++++++++++++++++++ 11 files changed, 361 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/almost_complete_letter_range.rs create mode 100644 tests/ui/almost_complete_letter_range.fixed create mode 100644 tests/ui/almost_complete_letter_range.rs create mode 100644 tests/ui/almost_complete_letter_range.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 3aaa79a1719c2..67ca4cf708aeb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3276,6 +3276,7 @@ Released 2018-09-13 [`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons [`allow_attributes_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes_without_reason +[`almost_complete_letter_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_letter_range [`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped [`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant [`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions diff --git a/clippy_lints/src/almost_complete_letter_range.rs b/clippy_lints/src/almost_complete_letter_range.rs new file mode 100644 index 0000000000000..b364a370efab5 --- /dev/null +++ b/clippy_lints/src/almost_complete_letter_range.rs @@ -0,0 +1,100 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::{trim_span, walk_span_to_context}; +use clippy_utils::{meets_msrv, msrvs}; +use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// Checks for ranges which almost include the entire range of letters from 'a' to 'z', but + /// don't because they're a half open range. + /// + /// ### Why is this bad? + /// This (`'a'..'z'`) is almost certainly a typo meant to include all letters. + /// + /// ### Example + /// ```rust + /// let _ = 'a'..'z'; + /// ``` + /// Use instead: + /// ```rust + /// let _ = 'a'..='z'; + /// ``` + #[clippy::version = "1.63.0"] + pub ALMOST_COMPLETE_LETTER_RANGE, + suspicious, + "almost complete letter range" +} +impl_lint_pass!(AlmostCompleteLetterRange => [ALMOST_COMPLETE_LETTER_RANGE]); + +pub struct AlmostCompleteLetterRange { + msrv: Option, +} +impl AlmostCompleteLetterRange { + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} +impl EarlyLintPass for AlmostCompleteLetterRange { + fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) { + if let ExprKind::Range(Some(start), Some(end), RangeLimits::HalfOpen) = &e.kind { + let ctxt = e.span.ctxt(); + let sugg = if let Some(start) = walk_span_to_context(start.span, ctxt) + && let Some(end) = walk_span_to_context(end.span, ctxt) + && meets_msrv(self.msrv, msrvs::RANGE_INCLUSIVE) + { + Some((trim_span(cx.sess().source_map(), start.between(end)), "..=")) + } else { + None + }; + check_range(cx, e.span, start, end, sugg); + } + } + + fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &Pat) { + if let PatKind::Range(Some(start), Some(end), kind) = &p.kind + && matches!(kind.node, RangeEnd::Excluded) + { + let sugg = if meets_msrv(self.msrv, msrvs::RANGE_INCLUSIVE) { + "..=" + } else { + "..." + }; + check_range(cx, p.span, start, end, Some((kind.span, sugg))); + } + } + + extract_msrv_attr!(EarlyContext); +} + +fn check_range(cx: &EarlyContext<'_>, span: Span, start: &Expr, end: &Expr, sugg: Option<(Span, &str)>) { + if let ExprKind::Lit(start_lit) = &start.peel_parens().kind + && let ExprKind::Lit(end_lit) = &end.peel_parens().kind + && matches!( + (&start_lit.kind, &end_lit.kind), + (LitKind::Byte(b'a') | LitKind::Char('a'), LitKind::Byte(b'z') | LitKind::Char('z')) + | (LitKind::Byte(b'A') | LitKind::Char('A'), LitKind::Byte(b'Z') | LitKind::Char('Z')) + ) + { + span_lint_and_then( + cx, + ALMOST_COMPLETE_LETTER_RANGE, + span, + "almost complete ascii letter range", + |diag| { + if let Some((span, sugg)) = sugg { + diag.span_suggestion( + span, + "use an inclusive range", + sugg.to_owned(), + Applicability::MaybeIncorrect, + ); + } + } + ); + } +} diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index a028b41db7740..d4f8d676c22d0 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -4,6 +4,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS), + LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE), LintId::of(approx_const::APPROX_CONSTANT), LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS), LintId::of(assign_ops::ASSIGN_OP_PATTERN), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 1f2132cf62003..b76eaae377e32 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -34,6 +34,7 @@ store.register_lints(&[ #[cfg(feature = "internal")] utils::internal_lints::UNNECESSARY_SYMBOL_STR, absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS, + almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE, approx_const::APPROX_CONSTANT, arithmetic::FLOAT_ARITHMETIC, arithmetic::INTEGER_ARITHMETIC, diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs index 20bf5a245b157..3d6d2d8951c23 100644 --- a/clippy_lints/src/lib.register_suspicious.rs +++ b/clippy_lints/src/lib.register_suspicious.rs @@ -3,6 +3,7 @@ // Manual edits will be overwritten. store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec![ + LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE), LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP), LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d611600cf711b..67ae2053f5ae0 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -168,6 +168,7 @@ mod renamed_lints; // begin lints modules, do not remove this comment, it’s used in `update_lints` mod absurd_extreme_comparisons; +mod almost_complete_letter_range; mod approx_const; mod arithmetic; mod as_conversions; @@ -911,6 +912,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| Box::new(duplicate_mod::DuplicateMod::default())); store.register_late_pass(|| Box::new(get_first::GetFirst)); store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding)); + store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 134fd1ce505a0..b9ec2c19fdd31 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -30,6 +30,7 @@ msrv_aliases! { 1,34,0 { TRY_FROM } 1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES } 1,28,0 { FROM_BOOL } + 1,26,0 { RANGE_INCLUSIVE } 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR } 1,16,0 { STR_REPEAT } 1,24,0 { IS_ASCII_DIGIT } diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 04ef2f57447c6..f88a92fb11c11 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -8,7 +8,7 @@ use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LintContext}; use rustc_span::hygiene; use rustc_span::source_map::SourceMap; -use rustc_span::{BytePos, Pos, Span, SyntaxContext}; +use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext}; use std::borrow::Cow; /// Checks if the span starts with the given text. This will return false if the span crosses @@ -389,6 +389,27 @@ pub fn without_block_comments(lines: Vec<&str>) -> Vec<&str> { without } +/// Trims the whitespace from the start and the end of the span. +pub fn trim_span(sm: &SourceMap, span: Span) -> Span { + let data = span.data(); + let sf: &_ = &sm.lookup_source_file(data.lo); + let Some(src) = sf.src.as_deref() else { + return span; + }; + let Some(snip) = &src.get((data.lo - sf.start_pos).to_usize()..(data.hi - sf.start_pos).to_usize()) else { + return span; + }; + let trim_start = snip.len() - snip.trim_start().len(); + let trim_end = snip.len() - snip.trim_end().len(); + SpanData { + lo: data.lo + BytePos::from_usize(trim_start), + hi: data.hi - BytePos::from_usize(trim_end), + ctxt: data.ctxt, + parent: data.parent, + } + .span() +} + #[cfg(test)] mod test { use super::{reindent_multiline, without_block_comments}; diff --git a/tests/ui/almost_complete_letter_range.fixed b/tests/ui/almost_complete_letter_range.fixed new file mode 100644 index 0000000000000..39f8f0c29495b --- /dev/null +++ b/tests/ui/almost_complete_letter_range.fixed @@ -0,0 +1,66 @@ +// run-rustfix +// edition:2018 + +#![feature(custom_inner_attributes)] +#![feature(exclusive_range_pattern)] +#![feature(stmt_expr_attributes)] +#![warn(clippy::almost_complete_letter_range)] +#![allow(ellipsis_inclusive_range_patterns)] + +macro_rules! a { + () => { + 'a' + }; +} + +fn main() { + #[rustfmt::skip] + { + let _ = ('a') ..='z'; + let _ = 'A' ..= ('Z'); + } + + let _ = 'b'..'z'; + let _ = 'B'..'Z'; + + let _ = (b'a')..=(b'z'); + let _ = b'A'..=b'Z'; + + let _ = b'b'..b'z'; + let _ = b'B'..b'Z'; + + let _ = a!()..='z'; + + let _ = match 0u8 { + b'a'..=b'z' if true => 1, + b'A'..=b'Z' if true => 2, + b'b'..b'z' => 3, + b'B'..b'Z' => 4, + _ => 5, + }; + + let _ = match 'x' { + 'a'..='z' if true => 1, + 'A'..='Z' if true => 2, + 'b'..'z' => 3, + 'B'..'Z' => 4, + _ => 5, + }; +} + +fn _under_msrv() { + #![clippy::msrv = "1.25"] + let _ = match 'a' { + 'a'...'z' => 1, + _ => 2, + }; +} + +fn _meets_msrv() { + #![clippy::msrv = "1.26"] + let _ = 'a'..='z'; + let _ = match 'a' { + 'a'..='z' => 1, + _ => 2, + }; +} diff --git a/tests/ui/almost_complete_letter_range.rs b/tests/ui/almost_complete_letter_range.rs new file mode 100644 index 0000000000000..3dc0219925760 --- /dev/null +++ b/tests/ui/almost_complete_letter_range.rs @@ -0,0 +1,66 @@ +// run-rustfix +// edition:2018 + +#![feature(custom_inner_attributes)] +#![feature(exclusive_range_pattern)] +#![feature(stmt_expr_attributes)] +#![warn(clippy::almost_complete_letter_range)] +#![allow(ellipsis_inclusive_range_patterns)] + +macro_rules! a { + () => { + 'a' + }; +} + +fn main() { + #[rustfmt::skip] + { + let _ = ('a') ..'z'; + let _ = 'A' .. ('Z'); + } + + let _ = 'b'..'z'; + let _ = 'B'..'Z'; + + let _ = (b'a')..(b'z'); + let _ = b'A'..b'Z'; + + let _ = b'b'..b'z'; + let _ = b'B'..b'Z'; + + let _ = a!()..'z'; + + let _ = match 0u8 { + b'a'..b'z' if true => 1, + b'A'..b'Z' if true => 2, + b'b'..b'z' => 3, + b'B'..b'Z' => 4, + _ => 5, + }; + + let _ = match 'x' { + 'a'..'z' if true => 1, + 'A'..'Z' if true => 2, + 'b'..'z' => 3, + 'B'..'Z' => 4, + _ => 5, + }; +} + +fn _under_msrv() { + #![clippy::msrv = "1.25"] + let _ = match 'a' { + 'a'..'z' => 1, + _ => 2, + }; +} + +fn _meets_msrv() { + #![clippy::msrv = "1.26"] + let _ = 'a'..'z'; + let _ = match 'a' { + 'a'..'z' => 1, + _ => 2, + }; +} diff --git a/tests/ui/almost_complete_letter_range.stderr b/tests/ui/almost_complete_letter_range.stderr new file mode 100644 index 0000000000000..74980ec1a923f --- /dev/null +++ b/tests/ui/almost_complete_letter_range.stderr @@ -0,0 +1,100 @@ +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:19:17 + | +LL | let _ = ('a') ..'z'; + | ^^^^^^--^^^ + | | + | help: use an inclusive range: `..=` + | + = note: `-D clippy::almost-complete-letter-range` implied by `-D warnings` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:20:17 + | +LL | let _ = 'A' .. ('Z'); + | ^^^^--^^^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:26:13 + | +LL | let _ = (b'a')..(b'z'); + | ^^^^^^--^^^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:27:13 + | +LL | let _ = b'A'..b'Z'; + | ^^^^--^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:32:13 + | +LL | let _ = a!()..'z'; + | ^^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:35:9 + | +LL | b'a'..b'z' if true => 1, + | ^^^^--^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:36:9 + | +LL | b'A'..b'Z' if true => 2, + | ^^^^--^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:43:9 + | +LL | 'a'..'z' if true => 1, + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:44:9 + | +LL | 'A'..'Z' if true => 2, + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:54:9 + | +LL | 'a'..'z' => 1, + | ^^^--^^^ + | | + | help: use an inclusive range: `...` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:61:13 + | +LL | let _ = 'a'..'z'; + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:63:9 + | +LL | 'a'..'z' => 1, + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: aborting due to 12 previous errors + From 9c9cca3e6672784f6b71b2113d27e2f7f707f650 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 18 Mar 2022 00:58:03 -0400 Subject: [PATCH 54/79] Don't lint `useless_transmute` on types with erased regions --- .../src/transmute/useless_transmute.rs | 47 +++++----- tests/ui/transmute.rs | 19 +++- tests/ui/transmute.stderr | 88 +++++++++---------- 3 files changed, 84 insertions(+), 70 deletions(-) diff --git a/clippy_lints/src/transmute/useless_transmute.rs b/clippy_lints/src/transmute/useless_transmute.rs index 3cc3d40a143dc..a0d104e239042 100644 --- a/clippy_lints/src/transmute/useless_transmute.rs +++ b/clippy_lints/src/transmute/useless_transmute.rs @@ -4,7 +4,7 @@ use clippy_utils::sugg; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, TypeFoldable}; /// Checks for `useless_transmute` lint. /// Returns `true` if it's triggered, otherwise returns `false`. @@ -16,7 +16,7 @@ pub(super) fn check<'tcx>( arg: &'tcx Expr<'_>, ) -> bool { match (&from_ty.kind(), &to_ty.kind()) { - _ if from_ty == to_ty => { + _ if from_ty == to_ty && !from_ty.has_erased_regions() => { span_lint( cx, USELESS_TRANSMUTE, @@ -26,28 +26,31 @@ pub(super) fn check<'tcx>( true }, (ty::Ref(_, rty, rty_mutbl), ty::RawPtr(ptr_ty)) => { - span_lint_and_then( - cx, - USELESS_TRANSMUTE, - e.span, - "transmute from a reference to a pointer", - |diag| { - if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { - let rty_and_mut = ty::TypeAndMut { - ty: *rty, - mutbl: *rty_mutbl, - }; + // No way to give the correct suggestion here. Avoid linting for now. + if !rty.has_erased_regions() { + span_lint_and_then( + cx, + USELESS_TRANSMUTE, + e.span, + "transmute from a reference to a pointer", + |diag| { + if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { + let rty_and_mut = ty::TypeAndMut { + ty: *rty, + mutbl: *rty_mutbl, + }; - let sugg = if *ptr_ty == rty_and_mut { - arg.as_ty(to_ty) - } else { - arg.as_ty(cx.tcx.mk_ptr(rty_and_mut)).as_ty(to_ty) - }; + let sugg = if *ptr_ty == rty_and_mut { + arg.as_ty(to_ty) + } else { + arg.as_ty(cx.tcx.mk_ptr(rty_and_mut)).as_ty(to_ty) + }; - diag.span_suggestion(e.span, "try", sugg.to_string(), Applicability::Unspecified); - } - }, - ); + diag.span_suggestion(e.span, "try", sugg.to_string(), Applicability::Unspecified); + } + }, + ); + } true }, (ty::Int(_) | ty::Uint(_), ty::RawPtr(_)) => { diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs index 5b688ce4d644f..001c910239aff 100644 --- a/tests/ui/transmute.rs +++ b/tests/ui/transmute.rs @@ -16,7 +16,8 @@ fn my_vec() -> MyVec { #[allow(clippy::needless_lifetimes, clippy::transmute_ptr_to_ptr)] #[warn(clippy::useless_transmute)] unsafe fn _generic<'a, T, U: 'a>(t: &'a T) { - let _: &'a T = core::intrinsics::transmute(t); + // FIXME: should lint + // let _: &'a T = core::intrinsics::transmute(t); let _: &'a U = core::intrinsics::transmute(t); @@ -48,6 +49,22 @@ fn useless() { let _ = (1 + 1_usize) as *const usize; } + + unsafe fn _f<'a, 'b>(x: &'a u32) -> &'b u32 { + std::mem::transmute(x) + } + + unsafe fn _f2<'a, 'b>(x: *const (dyn Iterator + 'a)) -> *const (dyn Iterator + 'b) { + std::mem::transmute(x) + } + + unsafe fn _f3<'a, 'b>(x: fn(&'a u32)) -> fn(&'b u32) { + std::mem::transmute(x) + } + + unsafe fn _f4<'a, 'b>(x: std::borrow::Cow<'a, str>) -> std::borrow::Cow<'b, str> { + std::mem::transmute(x) + } } struct Usize(usize); diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index 72a386e8fa618..008b4a981d7e6 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -1,73 +1,67 @@ -error: transmute from a type (`&T`) to itself - --> $DIR/transmute.rs:19:20 - | -LL | let _: &'a T = core::intrinsics::transmute(t); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::useless-transmute` implied by `-D warnings` - error: transmute from a reference to a pointer - --> $DIR/transmute.rs:23:23 + --> $DIR/transmute.rs:24:23 | LL | let _: *const T = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T` + | + = note: `-D clippy::useless-transmute` implied by `-D warnings` error: transmute from a reference to a pointer - --> $DIR/transmute.rs:25:21 + --> $DIR/transmute.rs:26:21 | LL | let _: *mut T = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T` error: transmute from a reference to a pointer - --> $DIR/transmute.rs:27:23 + --> $DIR/transmute.rs:28:23 | LL | let _: *const U = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U` error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:33:27 + --> $DIR/transmute.rs:34:27 | LL | let _: Vec = core::intrinsics::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:35:27 + --> $DIR/transmute.rs:36:27 | LL | let _: Vec = core::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:37:27 + --> $DIR/transmute.rs:38:27 | LL | let _: Vec = std::intrinsics::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:39:27 + --> $DIR/transmute.rs:40:27 | LL | let _: Vec = std::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:41:27 + --> $DIR/transmute.rs:42:27 | LL | let _: Vec = my_transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^ error: transmute from an integer to a pointer - --> $DIR/transmute.rs:43:31 + --> $DIR/transmute.rs:44:31 | LL | let _: *const usize = std::mem::transmute(5_isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `5_isize as *const usize` error: transmute from an integer to a pointer - --> $DIR/transmute.rs:47:31 + --> $DIR/transmute.rs:48:31 | LL | let _: *const usize = std::mem::transmute(1 + 1usize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(1 + 1usize) as *const usize` error: transmute from a type (`*const Usize`) to the type that it points to (`Usize`) - --> $DIR/transmute.rs:62:24 + --> $DIR/transmute.rs:79:24 | LL | let _: Usize = core::intrinsics::transmute(int_const_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -75,25 +69,25 @@ LL | let _: Usize = core::intrinsics::transmute(int_const_ptr); = note: `-D clippy::crosspointer-transmute` implied by `-D warnings` error: transmute from a type (`*mut Usize`) to the type that it points to (`Usize`) - --> $DIR/transmute.rs:64:24 + --> $DIR/transmute.rs:81:24 | LL | let _: Usize = core::intrinsics::transmute(int_mut_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*const Usize`) - --> $DIR/transmute.rs:66:31 + --> $DIR/transmute.rs:83:31 | LL | let _: *const Usize = core::intrinsics::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`) - --> $DIR/transmute.rs:68:29 + --> $DIR/transmute.rs:85:29 | LL | let _: *mut Usize = core::intrinsics::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a `u32` to a `char` - --> $DIR/transmute.rs:74:28 + --> $DIR/transmute.rs:91:28 | LL | let _: char = unsafe { std::mem::transmute(0_u32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_u32).unwrap()` @@ -101,13 +95,13 @@ LL | let _: char = unsafe { std::mem::transmute(0_u32) }; = note: `-D clippy::transmute-int-to-char` implied by `-D warnings` error: transmute from a `i32` to a `char` - --> $DIR/transmute.rs:75:28 + --> $DIR/transmute.rs:92:28 | LL | let _: char = unsafe { std::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()` error: transmute from a `u8` to a `bool` - --> $DIR/transmute.rs:84:28 + --> $DIR/transmute.rs:101:28 | LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0` @@ -115,7 +109,7 @@ LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; = note: `-D clippy::transmute-int-to-bool` implied by `-D warnings` error: transmute from a `u32` to a `f32` - --> $DIR/transmute.rs:90:31 + --> $DIR/transmute.rs:107:31 | LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` @@ -123,25 +117,25 @@ LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; = note: `-D clippy::transmute-int-to-float` implied by `-D warnings` error: transmute from a `i32` to a `f32` - --> $DIR/transmute.rs:91:31 + --> $DIR/transmute.rs:108:31 | LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` error: transmute from a `u64` to a `f64` - --> $DIR/transmute.rs:92:31 + --> $DIR/transmute.rs:109:31 | LL | let _: f64 = unsafe { std::mem::transmute(0_u64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_u64)` error: transmute from a `i64` to a `f64` - --> $DIR/transmute.rs:93:31 + --> $DIR/transmute.rs:110:31 | LL | let _: f64 = unsafe { std::mem::transmute(0_i64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)` error: transmute from a `u8` to a `[u8; 1]` - --> $DIR/transmute.rs:113:30 + --> $DIR/transmute.rs:130:30 | LL | let _: [u8; 1] = std::mem::transmute(0u8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` @@ -149,85 +143,85 @@ LL | let _: [u8; 1] = std::mem::transmute(0u8); = note: `-D clippy::transmute-num-to-bytes` implied by `-D warnings` error: transmute from a `u32` to a `[u8; 4]` - --> $DIR/transmute.rs:114:30 + --> $DIR/transmute.rs:131:30 | LL | let _: [u8; 4] = std::mem::transmute(0u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` error: transmute from a `u128` to a `[u8; 16]` - --> $DIR/transmute.rs:115:31 + --> $DIR/transmute.rs:132:31 | LL | let _: [u8; 16] = std::mem::transmute(0u128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` error: transmute from a `i8` to a `[u8; 1]` - --> $DIR/transmute.rs:116:30 + --> $DIR/transmute.rs:133:30 | LL | let _: [u8; 1] = std::mem::transmute(0i8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` error: transmute from a `i32` to a `[u8; 4]` - --> $DIR/transmute.rs:117:30 + --> $DIR/transmute.rs:134:30 | LL | let _: [u8; 4] = std::mem::transmute(0i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` error: transmute from a `i128` to a `[u8; 16]` - --> $DIR/transmute.rs:118:31 + --> $DIR/transmute.rs:135:31 | LL | let _: [u8; 16] = std::mem::transmute(0i128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` error: transmute from a `f32` to a `[u8; 4]` - --> $DIR/transmute.rs:119:30 + --> $DIR/transmute.rs:136:30 | LL | let _: [u8; 4] = std::mem::transmute(0.0f32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f32.to_ne_bytes()` error: transmute from a `f64` to a `[u8; 8]` - --> $DIR/transmute.rs:120:30 + --> $DIR/transmute.rs:137:30 | LL | let _: [u8; 8] = std::mem::transmute(0.0f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f64.to_ne_bytes()` error: transmute from a `u8` to a `[u8; 1]` - --> $DIR/transmute.rs:125:30 + --> $DIR/transmute.rs:142:30 | LL | let _: [u8; 1] = std::mem::transmute(0u8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` error: transmute from a `u32` to a `[u8; 4]` - --> $DIR/transmute.rs:126:30 + --> $DIR/transmute.rs:143:30 | LL | let _: [u8; 4] = std::mem::transmute(0u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` error: transmute from a `u128` to a `[u8; 16]` - --> $DIR/transmute.rs:127:31 + --> $DIR/transmute.rs:144:31 | LL | let _: [u8; 16] = std::mem::transmute(0u128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` error: transmute from a `i8` to a `[u8; 1]` - --> $DIR/transmute.rs:128:30 + --> $DIR/transmute.rs:145:30 | LL | let _: [u8; 1] = std::mem::transmute(0i8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` error: transmute from a `i32` to a `[u8; 4]` - --> $DIR/transmute.rs:129:30 + --> $DIR/transmute.rs:146:30 | LL | let _: [u8; 4] = std::mem::transmute(0i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` error: transmute from a `i128` to a `[u8; 16]` - --> $DIR/transmute.rs:130:31 + --> $DIR/transmute.rs:147:31 | LL | let _: [u8; 16] = std::mem::transmute(0i128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` error: transmute from a `&[u8]` to a `&str` - --> $DIR/transmute.rs:140:28 + --> $DIR/transmute.rs:157:28 | LL | let _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(B).unwrap()` @@ -235,16 +229,16 @@ LL | let _: &str = unsafe { std::mem::transmute(B) }; = note: `-D clippy::transmute-bytes-to-str` implied by `-D warnings` error: transmute from a `&mut [u8]` to a `&mut str` - --> $DIR/transmute.rs:141:32 + --> $DIR/transmute.rs:158:32 | LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` error: transmute from a `&[u8]` to a `&str` - --> $DIR/transmute.rs:142:30 + --> $DIR/transmute.rs:159:30 | LL | const _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_unchecked(B)` -error: aborting due to 39 previous errors +error: aborting due to 38 previous errors From 0c6ebf1c6abd86c92c063e03fa38de247eae8782 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 18 Mar 2022 00:59:48 -0400 Subject: [PATCH 55/79] Move `useless_transmute` back to `complexity` --- clippy_lints/src/lib.register_all.rs | 1 + clippy_lints/src/lib.register_complexity.rs | 1 + clippy_lints/src/lib.register_nursery.rs | 1 - clippy_lints/src/transmute/mod.rs | 2 +- tests/ui/transmute_undefined_repr.rs | 2 +- 5 files changed, 4 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index d4f8d676c22d0..8567b6b8a7e6a 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -305,6 +305,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES), LintId::of(transmute::TRANSMUTE_PTR_TO_REF), LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), + LintId::of(transmute::USELESS_TRANSMUTE), LintId::of(transmute::WRONG_TRANSMUTE), LintId::of(transmuting_null::TRANSMUTING_NULL), LintId::of(types::BORROWED_BOX), diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs index d5dfcd10a6683..fe8637faf9753 100644 --- a/clippy_lints/src/lib.register_complexity.rs +++ b/clippy_lints/src/lib.register_complexity.rs @@ -90,6 +90,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT), LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES), LintId::of(transmute::TRANSMUTE_PTR_TO_REF), + LintId::of(transmute::USELESS_TRANSMUTE), LintId::of(types::BORROWED_BOX), LintId::of(types::TYPE_COMPLEXITY), LintId::of(types::VEC_BOX), diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index 10808af363d70..f961952991f54 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -31,7 +31,6 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR), - LintId::of(transmute::USELESS_TRANSMUTE), LintId::of(unused_rounding::UNUSED_ROUNDING), LintId::of(use_self::USE_SELF), ]) diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index d2a040beb0cf7..cbe1406728bc7 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -59,7 +59,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "pre 1.29.0"] pub USELESS_TRANSMUTE, - nursery, + complexity, "transmutes that have the same to and from types or could be a cast/coercion" } diff --git a/tests/ui/transmute_undefined_repr.rs b/tests/ui/transmute_undefined_repr.rs index b06ed4a917376..ebcaa7a84cfb1 100644 --- a/tests/ui/transmute_undefined_repr.rs +++ b/tests/ui/transmute_undefined_repr.rs @@ -1,5 +1,5 @@ #![warn(clippy::transmute_undefined_repr)] -#![allow(clippy::unit_arg, clippy::transmute_ptr_to_ref)] +#![allow(clippy::unit_arg, clippy::transmute_ptr_to_ref, clippy::useless_transmute)] use core::any::TypeId; use core::ffi::c_void; From ca78e2428e25524831231263c579e390f7dbd1e3 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 30 May 2022 12:36:05 -0400 Subject: [PATCH 56/79] Add lint `swap_ptr_to_ref` --- CHANGELOG.md | 1 + clippy_lints/src/lib.register_all.rs | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_suspicious.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/swap_ptr_to_ref.rs | 80 +++++++++++++++++++++ clippy_utils/src/paths.rs | 1 + tests/ui/swap_ptr_to_ref.fixed | 24 +++++++ tests/ui/swap_ptr_to_ref.rs | 24 +++++++ tests/ui/swap_ptr_to_ref.stderr | 28 ++++++++ tests/ui/swap_ptr_to_ref_unfixable.rs | 18 +++++ tests/ui/swap_ptr_to_ref_unfixable.stderr | 22 ++++++ 12 files changed, 203 insertions(+) create mode 100644 clippy_lints/src/swap_ptr_to_ref.rs create mode 100644 tests/ui/swap_ptr_to_ref.fixed create mode 100644 tests/ui/swap_ptr_to_ref.rs create mode 100644 tests/ui/swap_ptr_to_ref.stderr create mode 100644 tests/ui/swap_ptr_to_ref_unfixable.rs create mode 100644 tests/ui/swap_ptr_to_ref_unfixable.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 67ca4cf708aeb..731d5cac3330b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3753,6 +3753,7 @@ Released 2018-09-13 [`suspicious_operation_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_operation_groupings [`suspicious_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_splitn [`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting +[`swap_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#swap_ptr_to_ref [`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment [`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 8567b6b8a7e6a..58f8ea7b997f9 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -292,6 +292,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), LintId::of(swap::ALMOST_SWAPPED), LintId::of(swap::MANUAL_SWAP), + LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF), LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT), LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index b76eaae377e32..e04d9f4fb9027 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -497,6 +497,7 @@ store.register_lints(&[ suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL, swap::ALMOST_SWAPPED, swap::MANUAL_SWAP, + swap_ptr_to_ref::SWAP_PTR_TO_REF, tabs_in_doc_comments::TABS_IN_DOC_COMMENTS, temporary_assignment::TEMPORARY_ASSIGNMENT, to_digit_is_some::TO_DIGIT_IS_SOME, diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs index 3d6d2d8951c23..43ced5070f17e 100644 --- a/clippy_lints/src/lib.register_suspicious.rs +++ b/clippy_lints/src/lib.register_suspicious.rs @@ -32,4 +32,5 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE), LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), + LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF), ]) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 67ae2053f5ae0..81a231cbebece 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -380,6 +380,7 @@ mod strlen_on_c_strings; mod suspicious_operation_groupings; mod suspicious_trait_impl; mod swap; +mod swap_ptr_to_ref; mod tabs_in_doc_comments; mod temporary_assignment; mod to_digit_is_some; @@ -913,6 +914,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(get_first::GetFirst)); store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding)); store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv))); + store.register_late_pass(|| Box::new(swap_ptr_to_ref::SwapPtrToRef)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/swap_ptr_to_ref.rs b/clippy_lints/src/swap_ptr_to_ref.rs new file mode 100644 index 0000000000000..75d3b040c968f --- /dev/null +++ b/clippy_lints/src/swap_ptr_to_ref.rs @@ -0,0 +1,80 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_with_context; +use clippy_utils::{match_def_path, path_def_id, paths}; +use rustc_errors::Applicability; +use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, UnOp}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{Span, SyntaxContext}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `core::mem::swap` where either parameter is derived from a pointer + /// + /// ### Why is this bad? + /// When at least one parameter to `swap` is derived from a pointer it may overlap with the + /// other. This would then lead to undefined behavior. + /// + /// ### Example + /// ```rust + /// unsafe fn swap(x: &[*mut u32], y: &[*mut u32]) { + /// for (&x, &y) in x.iter().zip(y) { + /// core::mem::swap(&mut *x, &mut *y); + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// unsafe fn swap(x: &[*mut u32], y: &[*mut u32]) { + /// for (&x, &y) in x.iter().zip(y) { + /// core::ptr::swap(x, y); + /// } + /// } + /// ``` + #[clippy::version = "1.63.0"] + pub SWAP_PTR_TO_REF, + suspicious, + "call to `mem::swap` using pointer derived references" +} +declare_lint_pass!(SwapPtrToRef => [SWAP_PTR_TO_REF]); + +impl LateLintPass<'_> for SwapPtrToRef { + fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) { + if let ExprKind::Call(fn_expr, [arg1, arg2]) = e.kind + && let Some(fn_id) = path_def_id(cx, fn_expr) + && match_def_path(cx, fn_id, &paths::MEM_SWAP) + && let ctxt = e.span.ctxt() + && let (from_ptr1, arg1_span) = is_ptr_to_ref(cx, arg1, ctxt) + && let (from_ptr2, arg2_span) = is_ptr_to_ref(cx, arg2, ctxt) + && (from_ptr1 || from_ptr2) + { + span_lint_and_then( + cx, + SWAP_PTR_TO_REF, + e.span, + "call to `core::mem::swap` with a parameter derived from a raw pointer", + |diag| { + if !((from_ptr1 && arg1_span.is_none()) || (from_ptr2 && arg2_span.is_none())) { + let mut app = Applicability::MachineApplicable; + let snip1 = snippet_with_context(cx, arg1_span.unwrap_or(arg1.span), ctxt, "..", &mut app).0; + let snip2 = snippet_with_context(cx, arg2_span.unwrap_or(arg2.span), ctxt, "..", &mut app).0; + diag.span_suggestion(e.span, "use ptr::swap", format!("core::ptr::swap({}, {})", snip1, snip2), app); + } + } + ); + } + } +} + +/// Checks if the expression converts a mutable pointer to a mutable reference. If it is, also +/// returns the span of the pointer expression if it's suitable for making a suggestion. +fn is_ptr_to_ref(cx: &LateContext<'_>, e: &Expr<'_>, ctxt: SyntaxContext) -> (bool, Option) { + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, borrowed_expr) = e.kind + && let ExprKind::Unary(UnOp::Deref, derefed_expr) = borrowed_expr.kind + && cx.typeck_results().expr_ty(derefed_expr).is_unsafe_ptr() + { + (true, (borrowed_expr.span.ctxt() == ctxt || derefed_expr.span.ctxt() == ctxt).then(|| derefed_expr.span)) + } else { + (false, None) + } +} diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 0064694ff929f..89789c3d85135 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -73,6 +73,7 @@ pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"]; pub const LATE_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "LateLintPass"]; #[cfg(feature = "internal")] pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"]; +pub const MEM_SWAP: [&str; 3] = ["core", "mem", "swap"]; pub const MUTEX_GUARD: [&str; 4] = ["std", "sync", "mutex", "MutexGuard"]; pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"]; /// Preferably use the diagnostic item `sym::Option` where possible diff --git a/tests/ui/swap_ptr_to_ref.fixed b/tests/ui/swap_ptr_to_ref.fixed new file mode 100644 index 0000000000000..596b6ee919bb4 --- /dev/null +++ b/tests/ui/swap_ptr_to_ref.fixed @@ -0,0 +1,24 @@ +// run-rustfix + +#![warn(clippy::swap_ptr_to_ref)] + +use core::ptr::addr_of_mut; + +fn main() { + let mut x = 0u32; + let y: *mut _ = &mut x; + let z: *mut _ = &mut x; + + unsafe { + core::ptr::swap(y, z); + core::ptr::swap(y, &mut x); + core::ptr::swap(&mut x, y); + core::ptr::swap(addr_of_mut!(x), addr_of_mut!(x)); + } + + let y = &mut x; + let mut z = 0u32; + let z = &mut z; + + core::mem::swap(y, z); +} diff --git a/tests/ui/swap_ptr_to_ref.rs b/tests/ui/swap_ptr_to_ref.rs new file mode 100644 index 0000000000000..282f571211d95 --- /dev/null +++ b/tests/ui/swap_ptr_to_ref.rs @@ -0,0 +1,24 @@ +// run-rustfix + +#![warn(clippy::swap_ptr_to_ref)] + +use core::ptr::addr_of_mut; + +fn main() { + let mut x = 0u32; + let y: *mut _ = &mut x; + let z: *mut _ = &mut x; + + unsafe { + core::mem::swap(&mut *y, &mut *z); + core::mem::swap(&mut *y, &mut x); + core::mem::swap(&mut x, &mut *y); + core::mem::swap(&mut *addr_of_mut!(x), &mut *addr_of_mut!(x)); + } + + let y = &mut x; + let mut z = 0u32; + let z = &mut z; + + core::mem::swap(y, z); +} diff --git a/tests/ui/swap_ptr_to_ref.stderr b/tests/ui/swap_ptr_to_ref.stderr new file mode 100644 index 0000000000000..401ce070869a2 --- /dev/null +++ b/tests/ui/swap_ptr_to_ref.stderr @@ -0,0 +1,28 @@ +error: call to `core::mem::swap` with a parameter derived from a raw pointer + --> $DIR/swap_ptr_to_ref.rs:13:9 + | +LL | core::mem::swap(&mut *y, &mut *z); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use ptr::swap: `core::ptr::swap(y, z)` + | + = note: `-D clippy::swap-ptr-to-ref` implied by `-D warnings` + +error: call to `core::mem::swap` with a parameter derived from a raw pointer + --> $DIR/swap_ptr_to_ref.rs:14:9 + | +LL | core::mem::swap(&mut *y, &mut x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use ptr::swap: `core::ptr::swap(y, &mut x)` + +error: call to `core::mem::swap` with a parameter derived from a raw pointer + --> $DIR/swap_ptr_to_ref.rs:15:9 + | +LL | core::mem::swap(&mut x, &mut *y); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use ptr::swap: `core::ptr::swap(&mut x, y)` + +error: call to `core::mem::swap` with a parameter derived from a raw pointer + --> $DIR/swap_ptr_to_ref.rs:16:9 + | +LL | core::mem::swap(&mut *addr_of_mut!(x), &mut *addr_of_mut!(x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use ptr::swap: `core::ptr::swap(addr_of_mut!(x), addr_of_mut!(x))` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/swap_ptr_to_ref_unfixable.rs b/tests/ui/swap_ptr_to_ref_unfixable.rs new file mode 100644 index 0000000000000..66ea7c6529bd2 --- /dev/null +++ b/tests/ui/swap_ptr_to_ref_unfixable.rs @@ -0,0 +1,18 @@ +#![warn(clippy::swap_ptr_to_ref)] + +macro_rules! addr_of_mut_to_ref { + ($e:expr) => { + &mut *core::ptr::addr_of_mut!($e) + }; +} + +fn main() { + let mut x = 0u32; + let y: *mut _ = &mut x; + + unsafe { + core::mem::swap(addr_of_mut_to_ref!(x), &mut *y); + core::mem::swap(&mut *y, addr_of_mut_to_ref!(x)); + core::mem::swap(addr_of_mut_to_ref!(x), addr_of_mut_to_ref!(x)); + } +} diff --git a/tests/ui/swap_ptr_to_ref_unfixable.stderr b/tests/ui/swap_ptr_to_ref_unfixable.stderr new file mode 100644 index 0000000000000..c261205d556e4 --- /dev/null +++ b/tests/ui/swap_ptr_to_ref_unfixable.stderr @@ -0,0 +1,22 @@ +error: call to `core::mem::swap` with a parameter derived from a raw pointer + --> $DIR/swap_ptr_to_ref_unfixable.rs:14:9 + | +LL | core::mem::swap(addr_of_mut_to_ref!(x), &mut *y); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::swap-ptr-to-ref` implied by `-D warnings` + +error: call to `core::mem::swap` with a parameter derived from a raw pointer + --> $DIR/swap_ptr_to_ref_unfixable.rs:15:9 + | +LL | core::mem::swap(&mut *y, addr_of_mut_to_ref!(x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: call to `core::mem::swap` with a parameter derived from a raw pointer + --> $DIR/swap_ptr_to_ref_unfixable.rs:16:9 + | +LL | core::mem::swap(addr_of_mut_to_ref!(x), addr_of_mut_to_ref!(x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + From 11d22ae7c572ce35923ced63415e95f0a2ab8361 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 30 May 2022 15:59:45 +1000 Subject: [PATCH 57/79] Lazify `SourceFile::lines`. `SourceFile::lines` is a big part of metadata. It's stored in a compressed form (a difference list) to save disk space. Decoding it is a big fraction of compile time for very small crates/programs. This commit introduces a new type `SourceFileLines` which has a `Lines` form and a `Diffs` form. The latter is used when the metadata is first read, and it is only decoded into the `Lines` form when line data is actually needed. This avoids the decoding cost for many files, especially in `std`. It's a performance win of up to 15% for tiny crates/programs where metadata decoding is a high part of compilation costs. A `Lock` is needed because the methods that access lines data (which can trigger decoding) take `&self` rather than `&mut self`. To allow for this, `SourceFile::lines` now takes a `FnMut` that operates on the lines slice rather than returning the lines slice. --- .../src/undocumented_unsafe_blocks.rs | 36 +++++++++++-------- clippy_utils/src/diagnostics.rs | 4 +-- clippy_utils/src/lib.rs | 2 +- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index 5a8677f90be41..025dd57e83aa5 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -187,11 +187,13 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> bool { && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf) && let Some(src) = unsafe_line.sf.src.as_deref() { - comment_start_line.line < unsafe_line.line && text_has_safety_comment( - src, - &unsafe_line.sf.lines[comment_start_line.line + 1..=unsafe_line.line], - unsafe_line.sf.start_pos.to_usize(), - ) + unsafe_line.sf.lines(|lines| { + comment_start_line.line < unsafe_line.line && text_has_safety_comment( + src, + &lines[comment_start_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos.to_usize(), + ) + }) } else { // Problem getting source text. Pretend a comment was found. true @@ -249,11 +251,13 @@ fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span && Lrc::ptr_eq(&unsafe_line.sf, ¯o_line.sf) && let Some(src) = unsafe_line.sf.src.as_deref() { - macro_line.line < unsafe_line.line && text_has_safety_comment( - src, - &unsafe_line.sf.lines[macro_line.line + 1..=unsafe_line.line], - unsafe_line.sf.start_pos.to_usize(), - ) + unsafe_line.sf.lines(|lines| { + macro_line.line < unsafe_line.line && text_has_safety_comment( + src, + &lines[macro_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos.to_usize(), + ) + }) } else { // Problem getting source text. Pretend a comment was found. true @@ -276,11 +280,13 @@ fn span_in_body_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { // Get the text from the start of function body to the unsafe block. // fn foo() { some_stuff; unsafe { stuff }; other_stuff; } // ^-------------^ - body_line.line < unsafe_line.line && text_has_safety_comment( - src, - &unsafe_line.sf.lines[body_line.line + 1..=unsafe_line.line], - unsafe_line.sf.start_pos.to_usize(), - ) + unsafe_line.sf.lines(|lines| { + body_line.line < unsafe_line.line && text_has_safety_comment( + src, + &lines[body_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos.to_usize(), + ) + }) } else { // Problem getting source text. Pretend a comment was found. true diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index 4e037d88494d7..39595f589c70b 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -283,10 +283,10 @@ pub fn span_lint_and_sugg_for_edges( { let split_idx = MAX_SUGGESTION_HIGHLIGHT_LINES / 2; let span_upper = sm.span_until_char( - sp.with_hi(line_upper.sf.lines[line_upper.line + split_idx]), + sp.with_hi(line_upper.sf.lines(|lines| lines[line_upper.line + split_idx])), '\n', ); - let span_bottom = sp.with_lo(line_bottom.sf.lines[line_bottom.line - split_idx]); + let span_bottom = sp.with_lo(line_bottom.sf.lines(|lines| lines[line_bottom.line - split_idx])); let sugg_lines_vec = sugg.lines().collect::>(); let sugg_upper = sugg_lines_vec[..split_idx].join("\n"); diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index adb37cc9d7510..833f8cde63aba 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1149,7 +1149,7 @@ fn line_span(cx: &T, span: Span) -> Span { let span = original_sp(span, DUMMY_SP); let source_map_and_line = cx.sess().source_map().lookup_line(span.lo()).unwrap(); let line_no = source_map_and_line.line; - let line_start = source_map_and_line.sf.lines[line_no]; + let line_start = source_map_and_line.sf.lines(|lines| lines[line_no]); span.with_lo(line_start) } From 202fdb9c530fa39ae90a23530e49b6746b1a4ec4 Mon Sep 17 00:00:00 2001 From: lengyijun Date: Wed, 1 Jun 2022 15:32:26 +0000 Subject: [PATCH 58/79] split into borrow_deref_ref.rs and borrow_deref_ref_unfixable.rs --- tests/compile-test.rs | 1 + tests/ui/borrow_deref_ref.fixed | 59 ++++++++++++++++++++++ tests/ui/borrow_deref_ref.rs | 5 +- tests/ui/borrow_deref_ref.stderr | 21 ++------ tests/ui/borrow_deref_ref_unfixable.rs | 10 ++++ tests/ui/borrow_deref_ref_unfixable.stderr | 18 +++++++ 6 files changed, 92 insertions(+), 22 deletions(-) create mode 100644 tests/ui/borrow_deref_ref.fixed create mode 100644 tests/ui/borrow_deref_ref_unfixable.rs create mode 100644 tests/ui/borrow_deref_ref_unfixable.stderr diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 3b599351016db..7d21983572324 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -346,6 +346,7 @@ fn compile_test() { const RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS: &[&str] = &[ "assign_ops2.rs", + "borrow_deref_ref_unfixable.rs", "cast_size_32bit.rs", "char_lit_as_u8.rs", "cmp_owned/without_suggestion.rs", diff --git a/tests/ui/borrow_deref_ref.fixed b/tests/ui/borrow_deref_ref.fixed new file mode 100644 index 0000000000000..bf4691c5bc97e --- /dev/null +++ b/tests/ui/borrow_deref_ref.fixed @@ -0,0 +1,59 @@ +// run-rustfix + +#![allow(dead_code, unused_variables)] + +fn main() {} + +mod should_lint { + fn one_help() { + let a = &12; + let b = a; + + let b = &mut bar(&12); + } + + fn bar(x: &u32) -> &u32 { + x + } +} + +// this mod explains why we should not lint `&mut &* (&T)` +mod should_not_lint1 { + fn foo(x: &mut &u32) { + *x = &1; + } + + fn main() { + let mut x = &0; + foo(&mut &*x); // should not lint + assert_eq!(*x, 0); + + foo(&mut x); + assert_eq!(*x, 1); + } +} + +// similar to should_not_lint1 +mod should_not_lint2 { + struct S<'a> { + a: &'a u32, + b: u32, + } + + fn main() { + let s = S { a: &1, b: 1 }; + let x = &mut &*s.a; + *x = &2; + } +} + +// this mod explains why we should not lint `& &* (&T)` +mod false_negative { + fn foo() { + let x = &12; + let addr_x = &x as *const _ as usize; + let addr_y = &x as *const _ as usize; // assert ok + // let addr_y = &x as *const _ as usize; // assert fail + assert_ne!(addr_x, addr_y); + } +} diff --git a/tests/ui/borrow_deref_ref.rs b/tests/ui/borrow_deref_ref.rs index cf502775b56cc..28c005fdbef70 100644 --- a/tests/ui/borrow_deref_ref.rs +++ b/tests/ui/borrow_deref_ref.rs @@ -5,13 +5,10 @@ fn main() {} mod should_lint { - fn foo() { + fn one_help() { let a = &12; let b = &*a; - let s = &String::new(); - let x: &str = &*s; - let b = &mut &*bar(&12); } diff --git a/tests/ui/borrow_deref_ref.stderr b/tests/ui/borrow_deref_ref.stderr index a34dbcc2e8afd..d72de37c69ff5 100644 --- a/tests/ui/borrow_deref_ref.stderr +++ b/tests/ui/borrow_deref_ref.stderr @@ -7,31 +7,16 @@ LL | let b = &*a; = note: `-D clippy::borrow-deref-ref` implied by `-D warnings` error: deref on an immutable reference - --> $DIR/borrow_deref_ref.rs:13:23 - | -LL | let x: &str = &*s; - | ^^^ - | -help: if you would like to reborrow, try removing `&*` - | -LL | let x: &str = s; - | ~ -help: if you would like to deref, try using `&**` - | -LL | let x: &str = &**s; - | ~~~~ - -error: deref on an immutable reference - --> $DIR/borrow_deref_ref.rs:15:22 + --> $DIR/borrow_deref_ref.rs:12:22 | LL | let b = &mut &*bar(&12); | ^^^^^^^^^^ help: if you would like to reborrow, try removing `&*`: `bar(&12)` error: deref on an immutable reference - --> $DIR/borrow_deref_ref.rs:58:23 + --> $DIR/borrow_deref_ref.rs:55:23 | LL | let addr_y = &&*x as *const _ as usize; // assert ok | ^^^ help: if you would like to reborrow, try removing `&*`: `x` -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/borrow_deref_ref_unfixable.rs b/tests/ui/borrow_deref_ref_unfixable.rs new file mode 100644 index 0000000000000..a8e2bbfef0f5a --- /dev/null +++ b/tests/ui/borrow_deref_ref_unfixable.rs @@ -0,0 +1,10 @@ +#![allow(dead_code, unused_variables)] + +fn main() {} + +mod should_lint { + fn two_helps() { + let s = &String::new(); + let x: &str = &*s; + } +} diff --git a/tests/ui/borrow_deref_ref_unfixable.stderr b/tests/ui/borrow_deref_ref_unfixable.stderr new file mode 100644 index 0000000000000..738b01e7ec1ee --- /dev/null +++ b/tests/ui/borrow_deref_ref_unfixable.stderr @@ -0,0 +1,18 @@ +error: deref on an immutable reference + --> $DIR/borrow_deref_ref_unfixable.rs:8:23 + | +LL | let x: &str = &*s; + | ^^^ + | + = note: `-D clippy::borrow-deref-ref` implied by `-D warnings` +help: if you would like to reborrow, try removing `&*` + | +LL | let x: &str = s; + | ~ +help: if you would like to deref, try using `&**` + | +LL | let x: &str = &**s; + | ~~~~ + +error: aborting due to previous error + From 86092a77b343474562780e2731ee430059e20570 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 1 Jun 2022 13:24:44 -0400 Subject: [PATCH 59/79] =?UTF-8?q?rename=20PointerAddress=20=E2=86=92=20Poi?= =?UTF-8?q?nterExposeAddress?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- clippy_utils/src/qualify_min_const_fn.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 283b20fc24d82..b1c82ac76e8eb 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -125,7 +125,7 @@ fn check_rvalue<'tcx>( Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => { check_place(tcx, *place, span, body) }, - Rvalue::Cast(CastKind::PointerAddress, _, _) => { + Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => { Err((span, "casting pointers to ints is unstable in const fn".into())) }, Rvalue::Cast(CastKind::Misc, operand, _) => { From b20f95c1a1780934367a019a6adba4ec74895716 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Wed, 1 Jun 2022 18:36:02 -0400 Subject: [PATCH 60/79] Combine doc examples --- clippy_lints/src/as_conversions.rs | 7 +- clippy_lints/src/attrs.rs | 6 +- clippy_lints/src/blocks_in_if_conditions.rs | 12 +-- clippy_lints/src/double_parens.rs | 14 ++- clippy_lints/src/eq_op.rs | 6 +- clippy_lints/src/loops/mod.rs | 15 ++-- clippy_lints/src/methods/mod.rs | 99 +++++++++------------ clippy_lints/src/minmax.rs | 8 +- clippy_lints/src/misc.rs | 14 ++- clippy_lints/src/mutable_debug_assertion.rs | 7 +- clippy_lints/src/trait_bounds.rs | 10 +-- 11 files changed, 88 insertions(+), 110 deletions(-) diff --git a/clippy_lints/src/as_conversions.rs b/clippy_lints/src/as_conversions.rs index 5fa8934c71bdd..6e5c8f445818e 100644 --- a/clippy_lints/src/as_conversions.rs +++ b/clippy_lints/src/as_conversions.rs @@ -32,12 +32,11 @@ declare_clippy_lint! { /// Use instead: /// ```rust,ignore /// f(a.try_into()?); - /// ``` - /// or - /// ```rust,ignore + /// + /// // or + /// /// f(a.try_into().expect("Unexpected u16 overflow in f")); /// ``` - /// #[clippy::version = "1.41.0"] pub AS_CONVERSIONS, restriction, diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 79a5b670fc8ed..770cb6a3d7b8b 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -241,13 +241,13 @@ declare_clippy_lint! { /// /// Use instead: /// ```rust + /// # mod hidden { /// #[cfg(target_os = "linux")] /// fn conditional() { } - /// ``` + /// # } /// - /// or + /// // or /// - /// ```rust /// #[cfg(unix)] /// fn conditional() { } /// ``` diff --git a/clippy_lints/src/blocks_in_if_conditions.rs b/clippy_lints/src/blocks_in_if_conditions.rs index 86e28ea7f7a92..5bd7a342389fe 100644 --- a/clippy_lints/src/blocks_in_if_conditions.rs +++ b/clippy_lints/src/blocks_in_if_conditions.rs @@ -22,21 +22,17 @@ declare_clippy_lint! { /// /// ### Examples /// ```rust - /// // Bad + /// # fn somefunc() -> bool { true }; /// if { true } { /* ... */ } /// - /// // Good - /// if true { /* ... */ } + /// if { let x = somefunc(); x } { /* ... */ } /// ``` /// - /// or - /// + /// Use instead: /// ```rust /// # fn somefunc() -> bool { true }; - /// // Bad - /// if { let x = somefunc(); x } { /* ... */ } + /// if true { /* ... */ } /// - /// // Good /// let res = { let x = somefunc(); x }; /// if res { /* ... */ } /// ``` diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index e10f740d24a41..a33ef5ce6e37c 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -13,23 +13,21 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// fn simple_double_parens() -> i32 { /// ((0)) /// } /// - /// // Good + /// # fn foo(bar: usize) {} + /// foo((0)); + /// ``` + /// + /// Use instead: + /// ```rust /// fn simple_no_parens() -> i32 { /// 0 /// } /// - /// // or - /// /// # fn foo(bar: usize) {} - /// // Bad - /// foo((0)); - /// - /// // Good /// foo(0); /// ``` #[clippy::version = "pre 1.29.0"] diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index afb5d32f95334..c3176d987c637 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -30,9 +30,9 @@ declare_clippy_lint! { /// ```rust /// # let x = 1; /// if x + 1 == x + 1 {} - /// ``` - /// or - /// ```rust + /// + /// // or + /// /// # let a = 3; /// # let b = 4; /// assert_eq!(a, a); diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 417248e31ae8c..d61be78895ffc 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -180,29 +180,24 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # let opt = Some(1); - /// - /// // Bad + /// # let res: Result = Ok(1); /// for x in opt { /// // .. /// } /// - /// // Good - /// if let Some(x) = opt { + /// for x in &res { /// // .. /// } /// ``` /// - /// or - /// + /// Use instead: /// ```rust + /// # let opt = Some(1); /// # let res: Result = Ok(1); - /// - /// // Bad - /// for x in &res { + /// if let Some(x) = opt { /// // .. /// } /// - /// // Good /// if let Ok(x) = res { /// // .. /// } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index eb1bce8c7d25b..08c89ceb79649 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -194,25 +194,18 @@ declare_clippy_lint! { /// /// ### Examples /// ```rust - /// # let opt = Some(1); - /// - /// // Bad - /// opt.unwrap(); - /// - /// // Good - /// opt.expect("more helpful message"); + /// # let option = Some(1); + /// # let result: Result = Ok(1); + /// option.unwrap(); + /// result.unwrap(); /// ``` /// - /// or - /// + /// Use instead: /// ```rust - /// # let res: Result = Ok(1); - /// - /// // Bad - /// res.unwrap(); - /// - /// // Good - /// res.expect("more helpful message"); + /// # let option = Some(1); + /// # let result: Result = Ok(1); + /// option.expect("more helpful message"); + /// result.expect("more helpful message"); /// ``` #[clippy::version = "1.45.0"] pub UNWRAP_USED, @@ -235,27 +228,21 @@ declare_clippy_lint! { /// /// ### Examples /// ```rust,ignore - /// # let opt = Some(1); - /// - /// // Bad - /// opt.expect("one"); - /// - /// // Good - /// let opt = Some(1); - /// opt?; + /// # let option = Some(1); + /// # let result: Result = Ok(1); + /// option.expect("one"); + /// result.expect("one"); /// ``` /// - /// or - /// - /// ```rust - /// # let res: Result = Ok(1); + /// Use instead: + /// ```rust,ignore + /// # let option = Some(1); + /// # let result: Result = Ok(1); + /// option?; /// - /// // Bad - /// res.expect("one"); + /// // or /// - /// // Good - /// res?; - /// # Ok::<(), ()>(()) + /// result?; /// ``` #[clippy::version = "1.45.0"] pub EXPECT_USED, @@ -431,26 +418,20 @@ declare_clippy_lint! { /// /// ### Examples /// ```rust - /// # let x = Some(1); - /// - /// // Bad - /// x.map(|a| a + 1).unwrap_or(0); - /// - /// // Good - /// x.map_or(0, |a| a + 1); + /// # let option = Some(1); + /// # let result: Result = Ok(1); + /// # fn some_function(foo: ()) -> usize { 1 } + /// option.map(|a| a + 1).unwrap_or(0); + /// result.map(|a| a + 1).unwrap_or_else(some_function); /// ``` /// - /// or - /// + /// Use instead: /// ```rust - /// # let x: Result = Ok(1); + /// # let option = Some(1); + /// # let result: Result = Ok(1); /// # fn some_function(foo: ()) -> usize { 1 } - /// - /// // Bad - /// x.map(|a| a + 1).unwrap_or_else(some_function); - /// - /// // Good - /// x.map_or_else(some_function, |a| a + 1); + /// option.map_or(0, |a| a + 1); + /// result.map_or_else(some_function, |a| a + 1); /// ``` #[clippy::version = "1.45.0"] pub MAP_UNWRAP_OR, @@ -793,13 +774,14 @@ declare_clippy_lint! { /// # let foo = Some(String::new()); /// foo.unwrap_or(String::new()); /// ``` - /// this can instead be written: + /// + /// Use instead: /// ```rust /// # let foo = Some(String::new()); /// foo.unwrap_or_else(String::new); - /// ``` - /// or - /// ```rust + /// + /// // or + /// /// # let foo = Some(String::new()); /// foo.unwrap_or_default(); /// ``` @@ -863,15 +845,14 @@ declare_clippy_lint! { /// # let err_code = "418"; /// # let err_msg = "I'm a teapot"; /// foo.expect(&format!("Err {}: {}", err_code, err_msg)); - /// ``` - /// or - /// ```rust + /// + /// // or + /// /// # let foo = Some(String::new()); - /// # let err_code = "418"; - /// # let err_msg = "I'm a teapot"; /// foo.expect(format!("Err {}: {}", err_code, err_msg).as_str()); /// ``` - /// this can instead be written: + /// + /// Use instead: /// ```rust /// # let foo = Some(String::new()); /// # let err_code = "418"; diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 65d1f440b7639..a081cde85725b 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -18,11 +18,11 @@ declare_clippy_lint! { /// the least it hurts readability of the code. /// /// ### Example - /// ```ignore + /// ```rust,ignore /// min(0, max(100, x)) - /// ``` - /// or - /// ```ignore + /// + /// // or + /// /// x.max(100).min(0) /// ``` /// It will always be equal to `0`. Probably the author meant to clamp the value diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 7fdc28c5a062d..5566569945322 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -103,11 +103,14 @@ declare_clippy_lint! { /// let x = 1.2331f64; /// let y = 1.2332f64; /// - /// // Bad /// if y == 1.23f64 { } /// if y != x {} // where both are floats + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let x = 1.2331f64; + /// # let y = 1.2332f64; /// let error_margin = f64::EPSILON; // Use an epsilon for comparison /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead. /// // let error_margin = std::f64::EPSILON; @@ -258,10 +261,13 @@ declare_clippy_lint! { /// let x: f64 = 1.0; /// const ONE: f64 = 1.00; /// - /// // Bad /// if x == ONE { } // where both are floats + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let x: f64 = 1.0; + /// # const ONE: f64 = 1.00; /// let error_margin = f64::EPSILON; // Use an epsilon for comparison /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead. /// // let error_margin = std::f64::EPSILON; diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index 8dba60f3a5854..321db08dfe89a 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -22,9 +22,12 @@ declare_clippy_lint! { /// ### Example /// ```rust,ignore /// debug_assert_eq!(vec![3].pop(), Some(3)); + /// /// // or - /// fn take_a_mut_parameter(_: &mut u32) -> bool { unimplemented!() } - /// debug_assert!(take_a_mut_parameter(&mut 5)); + /// + /// # let mut x = 5; + /// # fn takes_a_mut_parameter(_: &mut u32) -> bool { unimplemented!() } + /// debug_assert!(takes_a_mut_parameter(&mut x)); /// ``` #[clippy::version = "1.40.0"] pub DEBUG_ASSERT_WITH_MUT_CALL, diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 6c60fb4b8e029..b91be0eb4bed8 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -54,14 +54,14 @@ declare_clippy_lint! { /// fn func(arg: T) where T: Clone + Default {} /// ``` /// - /// Could be written as: - /// + /// Use instead: /// ```rust + /// # mod hidden { /// fn func(arg: T) {} - /// ``` - /// or + /// # } + /// + /// // or /// - /// ```rust /// fn func(arg: T) where T: Clone + Default {} /// ``` #[clippy::version = "1.47.0"] From bc5a8e9537d30858fede92347e3842d4d4f8a803 Mon Sep 17 00:00:00 2001 From: Preston From Date: Thu, 2 Jun 2022 02:23:42 -0600 Subject: [PATCH 61/79] Lint message correctly identifies match vs for loop --- .../src/significant_drop_in_scrutinee.rs | 23 +++++++++++-------- tests/ui/significant_drop_in_scrutinee.stderr | 2 +- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/significant_drop_in_scrutinee.rs b/clippy_lints/src/significant_drop_in_scrutinee.rs index 6b45b6d4452fb..2f7819cb47048 100644 --- a/clippy_lints/src/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/significant_drop_in_scrutinee.rs @@ -91,15 +91,11 @@ declare_lint_pass!(SignificantDropInScrutinee => [SIGNIFICANT_DROP_IN_SCRUTINEE] impl<'tcx> LateLintPass<'tcx> for SignificantDropInScrutinee { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if let Some(suggestions) = has_significant_drop_in_scrutinee(cx, expr) { + if let Some((suggestions, message)) = has_significant_drop_in_scrutinee(cx, expr) { for found in suggestions { - span_lint_and_then( - cx, - SIGNIFICANT_DROP_IN_SCRUTINEE, - found.found_span, - "temporary with significant drop in match scrutinee", - |diag| set_diagnostic(diag, cx, expr, found), - ); + span_lint_and_then(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, found.found_span, message, |diag| { + set_diagnostic(diag, cx, expr, found); + }); } } } @@ -153,13 +149,20 @@ fn set_diagnostic<'tcx>(diag: &mut Diagnostic, cx: &LateContext<'tcx>, expr: &'t fn has_significant_drop_in_scrutinee<'tcx, 'a>( cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>, -) -> Option> { +) -> Option<(Vec, &'static str)> { match expr.kind { ExprKind::Match(match_expr, _, source) => { match source { MatchSource::Normal | MatchSource::ForLoopDesugar => { let mut helper = SigDropHelper::new(cx); - helper.find_sig_drop(match_expr) + helper.find_sig_drop(match_expr).map(|drops| { + let message = if source == MatchSource::Normal { + "temporary with significant drop in match scrutinee" + } else { + "temporary with significant drop in for loop" + }; + (drops, message) + }) }, // MatchSource of TryDesugar or AwaitDesugar is out of scope for this lint MatchSource::TryDesugar | MatchSource::AwaitDesugar => None, diff --git a/tests/ui/significant_drop_in_scrutinee.stderr b/tests/ui/significant_drop_in_scrutinee.stderr index 77942325244d5..303f3c1df033c 100644 --- a/tests/ui/significant_drop_in_scrutinee.stderr +++ b/tests/ui/significant_drop_in_scrutinee.stderr @@ -285,7 +285,7 @@ error: temporary with significant drop in match scrutinee LL | match rwlock.read().unwrap().to_number() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: temporary with significant drop in match scrutinee +error: temporary with significant drop in for loop --> $DIR/significant_drop_in_scrutinee.rs:589:14 | LL | for s in rwlock.read().unwrap().iter() { From 756caf79e64598886551325dbf9ab7eccee03328 Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Tue, 31 May 2022 09:23:17 +0200 Subject: [PATCH 62/79] account for generics --- clippy_lints/src/large_enum_variant.rs | 24 ++++++++++++++++++++++-- tests/ui/large_enum_variant.rs | 14 ++++++++++++++ tests/ui/large_enum_variant.stderr | 24 +++++++++++++++++++++++- 3 files changed, 59 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index 34b7cf67db1d2..63ac092dfaf12 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -7,6 +7,7 @@ use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::{Adt, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; @@ -26,6 +27,15 @@ declare_clippy_lint! { /// the overhead is negligible and the boxing is counter-productive. Always /// measure the change this lint suggests. /// + /// For types that implement `Copy`, the suggestion to `Box` a variant's + /// data would require removing the trait impl. The types can of course + /// still be `Clone`, but that is worse ergonomically. Depending on the + /// use case it may be possible to store the large data in an auxillary + /// structure (e.g. Arena or ECS). + /// + /// The lint will ignore generic types if the layout depends on the + /// generics, even if the size difference will be large anyway. + /// /// ### Example /// ```rust /// // Bad @@ -74,7 +84,7 @@ struct VariantInfo { impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]); impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { - fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &Item<'tcx>) { if in_external_macro(cx.tcx.sess, item.span) { return; } @@ -132,7 +142,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { let fields = def.variants[variants_size[0].ind].data.fields(); variants_size[0].fields_size.sort_by(|a, b| (a.size.cmp(&b.size))); let mut applicability = Applicability::MaybeIncorrect; - if is_copy(cx, ty) { + if is_copy(cx, ty) || maybe_copy(cx, ty) { diag.span_note( item.ident.span, "boxing a variant would require the type no longer be `Copy`", @@ -176,3 +186,13 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { } } } + +fn maybe_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + if let Adt(_def, substs) = ty.kind() + && substs.types().next().is_some() + && let Some(copy_trait) = cx.tcx.lang_items().copy_trait() + { + return cx.tcx.non_blanket_impls_for_ty(copy_trait, ty).next().is_some(); + } + false +} diff --git a/tests/ui/large_enum_variant.rs b/tests/ui/large_enum_variant.rs index 42563383fedb5..23152a13322e8 100644 --- a/tests/ui/large_enum_variant.rs +++ b/tests/ui/large_enum_variant.rs @@ -114,8 +114,22 @@ impl Clone for ManuallyCopyLargeEnum { *self } } + impl Copy for ManuallyCopyLargeEnum {} +enum SomeGenericPossiblyCopyEnum { + A(bool, std::marker::PhantomData), + B([u64; 4000]), +} + +impl Clone for SomeGenericPossiblyCopyEnum { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for SomeGenericPossiblyCopyEnum {} + fn main() { large_enum_variant!(); } diff --git a/tests/ui/large_enum_variant.stderr b/tests/ui/large_enum_variant.stderr index 397d263ae9f95..0248327262da0 100644 --- a/tests/ui/large_enum_variant.stderr +++ b/tests/ui/large_enum_variant.stderr @@ -171,5 +171,27 @@ help: consider boxing the large fields to reduce the total size of the enum LL | B([u128; 4000]), | ^^^^^^^^^^^^^^^ -error: aborting due to 10 previous errors +error: large size difference between variants + --> $DIR/large_enum_variant.rs:122:5 + | +LL | B([u64; 4000]), + | ^^^^^^^^^^^^^^ this variant is 32000 bytes + | +note: and the second-largest variant is 1 bytes: + --> $DIR/large_enum_variant.rs:121:5 + | +LL | A(bool, std::marker::PhantomData), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: boxing a variant would require the type no longer be `Copy` + --> $DIR/large_enum_variant.rs:120:6 + | +LL | enum SomeGenericPossiblyCopyEnum { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider boxing the large fields to reduce the total size of the enum + --> $DIR/large_enum_variant.rs:122:5 + | +LL | B([u64; 4000]), + | ^^^^^^^^^^^^^^ + +error: aborting due to 11 previous errors From b885035ef7c5d9e4824944e2c50035afdb2fd2f5 Mon Sep 17 00:00:00 2001 From: dswij Date: Thu, 2 Jun 2022 20:16:46 +0800 Subject: [PATCH 63/79] `needless_return` checks for macro expr in return stmts --- clippy_lints/src/returns.rs | 14 +++++--------- tests/ui/needless_return.fixed | 10 ++++++++-- tests/ui/needless_return.rs | 6 ++++++ tests/ui/needless_return.stderr | 20 +++++++++++++++++++- 4 files changed, 38 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 8068fa22d9ccf..e525eba53e2aa 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::{snippet_opt, snippet_with_context}; use clippy_utils::{fn_def_id, path_to_local_id}; use if_chain::if_chain; use rustc_ast::ast::Attribute; @@ -226,14 +226,10 @@ fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option { - if in_external_macro(cx.tcx.sess, inner_span) || inner_span.from_expansion() { - return; - } - + let mut applicability = Applicability::MachineApplicable; span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| { - if let Some(snippet) = snippet_opt(cx, inner_span) { - diag.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable); - } + let (snippet, _) = snippet_with_context(cx, inner_span, ret_span.ctxt(), "..", &mut applicability); + diag.span_suggestion(ret_span, "remove `return`", snippet, applicability); }); }, None => match replacement { @@ -287,7 +283,7 @@ struct BorrowVisitor<'a, 'tcx> { impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.borrows { + if self.borrows || expr.span.from_expansion() { return; } diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index 603d438d55889..7c828430b7853 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -53,7 +53,7 @@ fn test_closure() { } fn test_macro_call() -> i32 { - return the_answer!(); + the_answer!() } fn test_void_fun() { @@ -175,7 +175,7 @@ async fn async_test_closure() { } async fn async_test_macro_call() -> i32 { - return the_answer!(); + the_answer!() } async fn async_test_void_fun() { @@ -223,4 +223,10 @@ fn let_else() { let Some(1) = Some(1) else { return }; } +fn needless_return_macro() -> String { + let _ = "foo"; + let _ = "bar"; + format!("Hello {}", "world!") +} + fn main() {} diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index c6c8cb9ec1520..fe82af00e6750 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -223,4 +223,10 @@ fn let_else() { let Some(1) = Some(1) else { return }; } +fn needless_return_macro() -> String { + let _ = "foo"; + let _ = "bar"; + return format!("Hello {}", "world!"); +} + fn main() {} diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index 5bc787c56a65b..4c8be47b025e2 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -48,6 +48,12 @@ error: unneeded `return` statement LL | let _ = || return true; | ^^^^^^^^^^^ help: remove `return`: `true` +error: unneeded `return` statement + --> $DIR/needless_return.rs:56:5 + | +LL | return the_answer!(); + | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `the_answer!()` + error: unneeded `return` statement --> $DIR/needless_return.rs:60:5 | @@ -168,6 +174,12 @@ error: unneeded `return` statement LL | let _ = || return true; | ^^^^^^^^^^^ help: remove `return`: `true` +error: unneeded `return` statement + --> $DIR/needless_return.rs:178:5 + | +LL | return the_answer!(); + | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `the_answer!()` + error: unneeded `return` statement --> $DIR/needless_return.rs:182:5 | @@ -204,5 +216,11 @@ error: unneeded `return` statement LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()` -error: aborting due to 34 previous errors +error: unneeded `return` statement + --> $DIR/needless_return.rs:229:5 + | +LL | return format!("Hello {}", "world!"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `format!("Hello {}", "world!")` + +error: aborting due to 37 previous errors From 678dcdd2be649e83eb1ce766f3ba141030f69e49 Mon Sep 17 00:00:00 2001 From: dswij Date: Thu, 2 Jun 2022 20:17:00 +0800 Subject: [PATCH 64/79] Apply `needless_return` suggestions --- clippy_lints/src/methods/option_map_or_none.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs index 76bc9466ed818..8989db54f6c5f 100644 --- a/clippy_lints/src/methods/option_map_or_none.rs +++ b/clippy_lints/src/methods/option_map_or_none.rs @@ -97,7 +97,7 @@ pub(super) fn check<'tcx>( let func_snippet = snippet(cx, map_arg.span, ".."); let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \ `and_then(..)` instead"; - return span_lint_and_sugg( + span_lint_and_sugg( cx, OPTION_MAP_OR_NONE, expr.span, @@ -110,7 +110,7 @@ pub(super) fn check<'tcx>( let msg = "called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling \ `ok()` instead"; let self_snippet = snippet(cx, recv.span, ".."); - return span_lint_and_sugg( + span_lint_and_sugg( cx, RESULT_MAP_OR_INTO_OPTION, expr.span, From 0600de4d127ce56984f90674c5dfff9b91bb6ba3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 2 Jun 2022 09:05:37 -0400 Subject: [PATCH 65/79] add cast kind of from_exposed_addr (int-to-ptr casts) --- clippy_utils/src/qualify_min_const_fn.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index b1c82ac76e8eb..58abef38ea8be 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -131,7 +131,12 @@ fn check_rvalue<'tcx>( Rvalue::Cast(CastKind::Misc, operand, _) => { check_operand(tcx, operand, span, body) }, - Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), operand, _) => { + Rvalue::Cast( + CastKind::PointerFromExposedAddress + | CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), + operand, + _ + ) => { check_operand(tcx, operand, span, body) }, Rvalue::Cast( From 58cd01c2fcda07f97efcd908b50e5256cf084593 Mon Sep 17 00:00:00 2001 From: Ariel Uy Date: Fri, 13 May 2022 23:48:52 -0700 Subject: [PATCH 66/79] Add new lint mismatching_type_param_order Add new lint for checking if type parameters are consistent between type definitions and impl blocks. --- CHANGELOG.md | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_pedantic.rs | 1 + clippy_lints/src/lib.rs | 2 + .../src/mismatching_type_param_order.rs | 116 ++++++++++++++++++ tests/ui/mismatching_type_param_order.rs | 60 +++++++++ tests/ui/mismatching_type_param_order.stderr | 83 +++++++++++++ 7 files changed, 264 insertions(+) create mode 100644 clippy_lints/src/mismatching_type_param_order.rs create mode 100644 tests/ui/mismatching_type_param_order.rs create mode 100644 tests/ui/mismatching_type_param_order.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c7ebfdf0355a..0a44ffdd3d4a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3560,6 +3560,7 @@ Released 2018-09-13 [`min_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#min_max [`misaligned_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#misaligned_transmute [`mismatched_target_os`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatched_target_os +[`mismatching_type_param_order`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatching_type_param_order [`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op [`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn [`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index ea8040a319b44..29bfc660d29fe 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -380,6 +380,7 @@ store.register_lints(&[ misc_early::UNNEEDED_WILDCARD_PATTERN, misc_early::UNSEPARATED_LITERAL_SUFFIX, misc_early::ZERO_PREFIXED_LITERAL, + mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER, missing_const_for_fn::MISSING_CONST_FOR_FN, missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS, missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES, diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs index 5684972b8bedf..2e47a287d5cf6 100644 --- a/clippy_lints/src/lib.register_pedantic.rs +++ b/clippy_lints/src/lib.register_pedantic.rs @@ -67,6 +67,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(methods::UNNECESSARY_JOIN), LintId::of(misc::FLOAT_CMP), LintId::of(misc::USED_UNDERSCORE_BINDING), + LintId::of(mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER), LintId::of(mut_mut::MUT_MUT), LintId::of(needless_bitwise_bool::NEEDLESS_BITWISE_BOOL), LintId::of(needless_continue::NEEDLESS_CONTINUE), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 89b65ce546971..6c3d7594f5c10 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -298,6 +298,7 @@ mod methods; mod minmax; mod misc; mod misc_early; +mod mismatching_type_param_order; mod missing_const_for_fn; mod missing_doc; mod missing_enforced_import_rename; @@ -917,6 +918,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding)); store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv))); store.register_late_pass(|| Box::new(swap_ptr_to_ref::SwapPtrToRef)); + store.register_late_pass(|| Box::new(mismatching_type_param_order::TypeParamMismatch)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/mismatching_type_param_order.rs b/clippy_lints/src/mismatching_type_param_order.rs new file mode 100644 index 0000000000000..d466d54a6ba5d --- /dev/null +++ b/clippy_lints/src/mismatching_type_param_order.rs @@ -0,0 +1,116 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{GenericArg, Item, ItemKind, QPath, Ty, TyKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::GenericParamDefKind; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for type parameters which are positioned inconsistently between + /// a type definition and impl block. Specifically, a paramater in an impl + /// block which has the same name as a parameter in the type def, but is in + /// a different place. + /// + /// ### Why is this bad? + /// Type parameters are determined by their position rather than name. + /// Naming type parameters inconsistently may cause you to refer to the + /// wrong type parameter. + /// + /// ### Example + /// ```rust + /// struct Foo { + /// x: A, + /// y: B, + /// } + /// // inside the impl, B refers to Foo::A + /// impl Foo {} + /// ``` + /// Use instead: + /// ```rust + /// struct Foo { + /// x: A, + /// y: B, + /// } + /// impl Foo {} + /// ``` + #[clippy::version = "1.62.0"] + pub MISMATCHING_TYPE_PARAM_ORDER, + pedantic, + "type parameter positioned inconsistently between type def and impl block" +} +declare_lint_pass!(TypeParamMismatch => [MISMATCHING_TYPE_PARAM_ORDER]); + +impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if_chain! { + if !item.span.from_expansion(); + if let ItemKind::Impl(imp) = &item.kind; + if let TyKind::Path(QPath::Resolved(_, path)) = &imp.self_ty.kind; + if let Some(segment) = path.segments.iter().next(); + if let Some(generic_args) = segment.args; + if !generic_args.args.is_empty(); + then { + // get the name and span of the generic parameters in the Impl + let impl_params = generic_args.args.iter() + .filter_map(|p| + match p { + GenericArg::Type(Ty {kind: TyKind::Path(QPath::Resolved(_, path)), ..}) => + Some((path.segments[0].ident.to_string(), path.span)), + _ => None, + } + ); + + // find the type that the Impl is for + // only lint on struct/enum/union for now + let defid = match path.res { + Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, defid) => defid, + _ => return, + }; + + // get the names of the generic parameters in the type + let type_params = &cx.tcx.generics_of(defid).params; + let type_param_names: Vec<_> = type_params.iter() + .filter_map(|p| + match p.kind { + GenericParamDefKind::Type {..} => Some(p.name.to_string()), + _ => None, + } + ).collect(); + // hashmap of name -> index for mismatch_param_name + let type_param_names_hashmap: FxHashMap<&String, usize> = + type_param_names.iter().enumerate().map(|(i, param)| (param, i)).collect(); + + let type_name = segment.ident; + for (i, (impl_param_name, impl_param_span)) in impl_params.enumerate() { + if mismatch_param_name(i, &impl_param_name, &type_param_names_hashmap) { + let msg = format!("`{}` has a similarly named generic type parameter `{}` in its declaration, but in a different order", + type_name, impl_param_name); + let help = format!("try `{}`, or a name that does not conflict with `{}`'s generic params", + type_param_names[i], type_name); + span_lint_and_help( + cx, + MISMATCHING_TYPE_PARAM_ORDER, + impl_param_span, + &msg, + None, + &help + ); + } + } + } + } + } +} + +// Checks if impl_param_name is the same as one of type_param_names, +// and is in a different position +fn mismatch_param_name(i: usize, impl_param_name: &String, type_param_names: &FxHashMap<&String, usize>) -> bool { + if let Some(j) = type_param_names.get(impl_param_name) { + if i != *j { + return true; + } + } + false +} diff --git a/tests/ui/mismatching_type_param_order.rs b/tests/ui/mismatching_type_param_order.rs new file mode 100644 index 0000000000000..8f286c9304ccb --- /dev/null +++ b/tests/ui/mismatching_type_param_order.rs @@ -0,0 +1,60 @@ +#![warn(clippy::mismatching_type_param_order)] +#![allow(clippy::blacklisted_name)] + +fn main() { + struct Foo { + x: A, + y: B, + } + + // lint on both params + impl Foo {} + + // lint on the 2nd param + impl Foo {} + + // should not lint + impl Foo {} + + struct FooLifetime<'l, 'm, A, B> { + x: &'l A, + y: &'m B, + } + + // should not lint on lifetimes + impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {} + + struct Bar { + x: i32, + } + + // should not lint + impl Bar {} + + // also works for enums + enum FooEnum { + X(A), + Y(B), + Z(C), + } + + impl FooEnum {} + + // also works for unions + union FooUnion + where + B: Copy, + { + x: A, + y: B, + } + + impl FooUnion where A: Copy {} + + impl FooUnion + where + A: Copy, + B: Copy, + { + } +} diff --git a/tests/ui/mismatching_type_param_order.stderr b/tests/ui/mismatching_type_param_order.stderr new file mode 100644 index 0000000000000..cb720256c50e5 --- /dev/null +++ b/tests/ui/mismatching_type_param_order.stderr @@ -0,0 +1,83 @@ +error: `Foo` has a similarly named generic type parameter `B` in its declaration, but in a different order + --> $DIR/mismatching_type_param_order.rs:11:20 + | +LL | impl Foo {} + | ^ + | + = note: `-D clippy::mismatching-type-param-order` implied by `-D warnings` + = help: try `A`, or a name that does not conflict with `Foo`'s generic params + +error: `Foo` has a similarly named generic type parameter `A` in its declaration, but in a different order + --> $DIR/mismatching_type_param_order.rs:11:23 + | +LL | impl Foo {} + | ^ + | + = help: try `B`, or a name that does not conflict with `Foo`'s generic params + +error: `Foo` has a similarly named generic type parameter `A` in its declaration, but in a different order + --> $DIR/mismatching_type_param_order.rs:14:23 + | +LL | impl Foo {} + | ^ + | + = help: try `B`, or a name that does not conflict with `Foo`'s generic params + +error: `FooLifetime` has a similarly named generic type parameter `B` in its declaration, but in a different order + --> $DIR/mismatching_type_param_order.rs:25:44 + | +LL | impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {} + | ^ + | + = help: try `A`, or a name that does not conflict with `FooLifetime`'s generic params + +error: `FooLifetime` has a similarly named generic type parameter `A` in its declaration, but in a different order + --> $DIR/mismatching_type_param_order.rs:25:47 + | +LL | impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {} + | ^ + | + = help: try `B`, or a name that does not conflict with `FooLifetime`'s generic params + +error: `FooEnum` has a similarly named generic type parameter `C` in its declaration, but in a different order + --> $DIR/mismatching_type_param_order.rs:41:27 + | +LL | impl FooEnum {} + | ^ + | + = help: try `A`, or a name that does not conflict with `FooEnum`'s generic params + +error: `FooEnum` has a similarly named generic type parameter `A` in its declaration, but in a different order + --> $DIR/mismatching_type_param_order.rs:41:30 + | +LL | impl FooEnum {} + | ^ + | + = help: try `B`, or a name that does not conflict with `FooEnum`'s generic params + +error: `FooEnum` has a similarly named generic type parameter `B` in its declaration, but in a different order + --> $DIR/mismatching_type_param_order.rs:41:33 + | +LL | impl FooEnum {} + | ^ + | + = help: try `C`, or a name that does not conflict with `FooEnum`'s generic params + +error: `FooUnion` has a similarly named generic type parameter `B` in its declaration, but in a different order + --> $DIR/mismatching_type_param_order.rs:52:31 + | +LL | impl FooUnion where A: Copy {} + | ^ + | + = help: try `A`, or a name that does not conflict with `FooUnion`'s generic params + +error: `FooUnion` has a similarly named generic type parameter `A` in its declaration, but in a different order + --> $DIR/mismatching_type_param_order.rs:52:34 + | +LL | impl FooUnion where A: Copy {} + | ^ + | + = help: try `B`, or a name that does not conflict with `FooUnion`'s generic params + +error: aborting due to 10 previous errors + From 1e86cc5194eca009519b9631d081867366fe5c44 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Wed, 27 Apr 2022 22:15:58 +0200 Subject: [PATCH 67/79] Manipulate lifetimes by LocalDefId for region resolution. --- clippy_lints/src/lifetimes.rs | 2 +- clippy_lints/src/ptr.rs | 2 +- clippy_utils/src/hir_utils.rs | 8 +++----- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 51d5b510ab930..070c7e591420d 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -371,7 +371,7 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> { if let Some(ref lt) = *lifetime { if lt.name == LifetimeName::Static { self.lts.push(RefLt::Static); - } else if let LifetimeName::Param(ParamName::Fresh(_)) = lt.name { + } else if let LifetimeName::Param(_, ParamName::Fresh) = lt.name { // Fresh lifetimes generated should be ignored. } else if lt.is_elided() { self.lts.push(RefLt::Unnamed); diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 548f7b2528b11..0b96f6ff68358 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -343,7 +343,7 @@ impl fmt::Display for RefPrefix { use fmt::Write; f.write_char('&')?; match self.lt { - LifetimeName::Param(ParamName::Plain(name)) => { + LifetimeName::Param(_, ParamName::Plain(name)) => { name.fmt(f)?; f.write_char(' ')?; }, diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index c440793b90e0e..fc1a4e1f60255 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -902,16 +902,14 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { pub fn hash_lifetime(&mut self, lifetime: Lifetime) { std::mem::discriminant(&lifetime.name).hash(&mut self.s); - if let LifetimeName::Param(ref name) = lifetime.name { + if let LifetimeName::Param(param_id, ref name) = lifetime.name { std::mem::discriminant(name).hash(&mut self.s); + param_id.hash(&mut self.s); match name { ParamName::Plain(ref ident) => { ident.name.hash(&mut self.s); }, - ParamName::Fresh(ref size) => { - size.hash(&mut self.s); - }, - ParamName::Error => {}, + ParamName::Fresh | ParamName::Error => {}, } } } From 64fe4e32db8c593d625a44063fca1755bba493ee Mon Sep 17 00:00:00 2001 From: DevAccentor Date: Fri, 3 Jun 2022 15:59:26 +0200 Subject: [PATCH 68/79] add as_underscore lint --- CHANGELOG.md | 1 + clippy_lints/src/as_underscore.rs | 74 ++++++++++++++++++++ clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_restriction.rs | 1 + clippy_lints/src/lib.rs | 2 + tests/ui/as_underscore.fixed | 13 ++++ tests/ui/as_underscore.rs | 13 ++++ tests/ui/as_underscore.stderr | 20 ++++++ 8 files changed, 125 insertions(+) create mode 100644 clippy_lints/src/as_underscore.rs create mode 100644 tests/ui/as_underscore.fixed create mode 100644 tests/ui/as_underscore.rs create mode 100644 tests/ui/as_underscore.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a44ffdd3d4a7..6ef338b819d4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3280,6 +3280,7 @@ Released 2018-09-13 [`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped [`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant [`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions +[`as_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_underscore [`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants [`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern [`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops diff --git a/clippy_lints/src/as_underscore.rs b/clippy_lints/src/as_underscore.rs new file mode 100644 index 0000000000000..464be4218dd4d --- /dev/null +++ b/clippy_lints/src/as_underscore.rs @@ -0,0 +1,74 @@ +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, TyKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Check for the usage of `as _` conversion using inferred type. + /// + /// ### Why is this bad? + /// The conversion might include lossy conversion and dangerous cast that might go + /// undetected du to the type being inferred. + /// + /// The lint is allowed by default as using `_` is less wordy than always specifying the type. + /// + /// ### Example + /// ```rust + /// fn foo(n: usize) {} + /// let n: u16 = 256; + /// foo(n as _); + /// ``` + /// Use instead: + /// ```rust + /// fn foo(n: usize) {} + /// let n: u16 = 256; + /// foo(n as usize); + /// ``` + #[clippy::version = "1.63.0"] + pub AS_UNDERSCORE, + restriction, + "detects `as _` conversion" +} +declare_lint_pass!(AsUnderscore => [AS_UNDERSCORE]); + +impl<'tcx> LateLintPass<'tcx> for AsUnderscore { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { + if in_external_macro(cx.sess(), expr.span) { + return; + } + + if let ExprKind::Cast(_, ty) = expr.kind && let TyKind::Infer = ty.kind { + + let ty_resolved = cx.typeck_results().expr_ty(expr); + if let ty::Error(_) = ty_resolved.kind() { + span_lint_and_help( + cx, + AS_UNDERSCORE, + expr.span, + "using `as _` conversion", + None, + "consider giving the type explicitly", + ); + } else { + span_lint_and_then( + cx, + AS_UNDERSCORE, + expr.span, + "using `as _` conversion", + |diag| { + diag.span_suggestion( + ty.span, + "consider giving the type explicitly", + format!("{}", ty_resolved), + Applicability::MachineApplicable, + ); + } + ); + } + } + } +} diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 29bfc660d29fe..f2e0083dea699 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -37,6 +37,7 @@ store.register_lints(&[ almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE, approx_const::APPROX_CONSTANT, as_conversions::AS_CONVERSIONS, + as_underscore::AS_UNDERSCORE, asm_syntax::INLINE_ASM_X86_ATT_SYNTAX, asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX, assertions_on_constants::ASSERTIONS_ON_CONSTANTS, diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs index 304b595541aa6..aaa83402ccc12 100644 --- a/clippy_lints/src/lib.register_restriction.rs +++ b/clippy_lints/src/lib.register_restriction.rs @@ -4,6 +4,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(as_conversions::AS_CONVERSIONS), + LintId::of(as_underscore::AS_UNDERSCORE), LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX), LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX), LintId::of(attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6c3d7594f5c10..235a998f75315 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -171,6 +171,7 @@ mod absurd_extreme_comparisons; mod almost_complete_letter_range; mod approx_const; mod as_conversions; +mod as_underscore; mod asm_syntax; mod assertions_on_constants; mod assign_ops; @@ -919,6 +920,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv))); store.register_late_pass(|| Box::new(swap_ptr_to_ref::SwapPtrToRef)); store.register_late_pass(|| Box::new(mismatching_type_param_order::TypeParamMismatch)); + store.register_late_pass(|| Box::new(as_underscore::AsUnderscore)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/tests/ui/as_underscore.fixed b/tests/ui/as_underscore.fixed new file mode 100644 index 0000000000000..948f6d8e6b10b --- /dev/null +++ b/tests/ui/as_underscore.fixed @@ -0,0 +1,13 @@ +// run-rustfix + +#![warn(clippy::as_underscore)] + +fn foo(_n: usize) {} + +fn main() { + let n: u16 = 256; + foo(n as usize); + + let n = 0_u128; + let _n: u8 = n as u8; +} diff --git a/tests/ui/as_underscore.rs b/tests/ui/as_underscore.rs new file mode 100644 index 0000000000000..97785ed08a8d0 --- /dev/null +++ b/tests/ui/as_underscore.rs @@ -0,0 +1,13 @@ +// run-rustfix + +#![warn(clippy::as_underscore)] + +fn foo(_n: usize) {} + +fn main() { + let n: u16 = 256; + foo(n as _); + + let n = 0_u128; + let _n: u8 = n as _; +} diff --git a/tests/ui/as_underscore.stderr b/tests/ui/as_underscore.stderr new file mode 100644 index 0000000000000..d7cd58d965acb --- /dev/null +++ b/tests/ui/as_underscore.stderr @@ -0,0 +1,20 @@ +error: using `as _` conversion + --> $DIR/as_underscore.rs:9:9 + | +LL | foo(n as _); + | ^^^^^- + | | + | help: consider giving the type explicitly: `usize` + | + = note: `-D clippy::as-underscore` implied by `-D warnings` + +error: using `as _` conversion + --> $DIR/as_underscore.rs:12:18 + | +LL | let _n: u8 = n as _; + | ^^^^^- + | | + | help: consider giving the type explicitly: `u8` + +error: aborting due to 2 previous errors + From 81e44502ac2e86258dbc880bdf702eab0030c4fb Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 3 Jun 2022 12:14:24 -0400 Subject: [PATCH 69/79] Merge `CollapsibleMatch` into `Matches` lint pass --- clippy_lints/src/lib.register_all.rs | 2 +- clippy_lints/src/lib.register_lints.rs | 2 +- clippy_lints/src/lib.register_style.rs | 2 +- clippy_lints/src/lib.rs | 2 - .../src/{ => matches}/collapsible_match.rs | 70 ++++----------- .../src/matches/match_like_matches.rs | 34 +++---- clippy_lints/src/matches/mod.rs | 90 ++++++++++++++++--- clippy_lints/src/matches/needless_match.rs | 30 +++---- .../src/matches/redundant_pattern_match.rs | 20 +++-- 9 files changed, 139 insertions(+), 113 deletions(-) rename clippy_lints/src/{ => matches}/collapsible_match.rs (71%) diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 51b3babef7f39..b1da36a0d4017 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -38,7 +38,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(casts::UNNECESSARY_CAST), LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF), LintId::of(collapsible_if::COLLAPSIBLE_IF), - LintId::of(collapsible_match::COLLAPSIBLE_MATCH), LintId::of(comparison_chain::COMPARISON_CHAIN), LintId::of(copies::IFS_SAME_COND), LintId::of(copies::IF_SAME_THEN_ELSE), @@ -143,6 +142,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), LintId::of(match_result_ok::MATCH_RESULT_OK), LintId::of(match_str_case_mismatch::MATCH_STR_CASE_MISMATCH), + LintId::of(matches::COLLAPSIBLE_MATCH), LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), LintId::of(matches::MATCH_AS_REF), LintId::of(matches::MATCH_LIKE_MATCHES_MACRO), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 29bfc660d29fe..58e18b710ea88 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -93,7 +93,6 @@ store.register_lints(&[ cognitive_complexity::COGNITIVE_COMPLEXITY, collapsible_if::COLLAPSIBLE_ELSE_IF, collapsible_if::COLLAPSIBLE_IF, - collapsible_match::COLLAPSIBLE_MATCH, comparison_chain::COMPARISON_CHAIN, copies::BRANCHES_SHARING_CODE, copies::IFS_SAME_COND, @@ -263,6 +262,7 @@ store.register_lints(&[ match_on_vec_items::MATCH_ON_VEC_ITEMS, match_result_ok::MATCH_RESULT_OK, match_str_case_mismatch::MATCH_STR_CASE_MISMATCH, + matches::COLLAPSIBLE_MATCH, matches::INFALLIBLE_DESTRUCTURING_MATCH, matches::MATCH_AS_REF, matches::MATCH_BOOL, diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index ea2e1082458c2..07e90b8b656e5 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -12,7 +12,6 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF), LintId::of(collapsible_if::COLLAPSIBLE_IF), - LintId::of(collapsible_match::COLLAPSIBLE_MATCH), LintId::of(comparison_chain::COMPARISON_CHAIN), LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), LintId::of(dereference::NEEDLESS_BORROW), @@ -50,6 +49,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(map_clone::MAP_CLONE), LintId::of(match_result_ok::MATCH_RESULT_OK), + LintId::of(matches::COLLAPSIBLE_MATCH), LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), LintId::of(matches::MATCH_LIKE_MATCHES_MACRO), LintId::of(matches::MATCH_OVERLAPPING_ARM), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6c3d7594f5c10..6e16d470ac878 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -192,7 +192,6 @@ mod casts; mod checked_conversions; mod cognitive_complexity; mod collapsible_if; -mod collapsible_match; mod comparison_chain; mod copies; mod copy_iterator; @@ -568,7 +567,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(len_zero::LenZero)); store.register_late_pass(|| Box::new(attrs::Attributes)); store.register_late_pass(|| Box::new(blocks_in_if_conditions::BlocksInIfConditions)); - store.register_late_pass(|| Box::new(collapsible_match::CollapsibleMatch)); store.register_late_pass(|| Box::new(unicode::Unicode)); store.register_late_pass(|| Box::new(uninit_vec::UninitVec)); store.register_late_pass(|| Box::new(unit_hash::UnitHash)); diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs similarity index 71% rename from clippy_lints/src/collapsible_match.rs rename to clippy_lints/src/matches/collapsible_match.rs index ec55009f347d3..07021f1bcad8d 100644 --- a/clippy_lints/src/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -6,68 +6,28 @@ use if_chain::if_chain; use rustc_errors::MultiSpan; use rustc_hir::LangItem::OptionNone; use rustc_hir::{Arm, Expr, Guard, HirId, Let, Pat, PatKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_lint::LateContext; use rustc_span::Span; -declare_clippy_lint! { - /// ### What it does - /// Finds nested `match` or `if let` expressions where the patterns may be "collapsed" together - /// without adding any branches. - /// - /// Note that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only - /// cases where merging would most likely make the code more readable. - /// - /// ### Why is this bad? - /// It is unnecessarily verbose and complex. - /// - /// ### Example - /// ```rust - /// fn func(opt: Option>) { - /// let n = match opt { - /// Some(n) => match n { - /// Ok(n) => n, - /// _ => return, - /// } - /// None => return, - /// }; - /// } - /// ``` - /// Use instead: - /// ```rust - /// fn func(opt: Option>) { - /// let n = match opt { - /// Some(Ok(n)) => n, - /// _ => return, - /// }; - /// } - /// ``` - #[clippy::version = "1.50.0"] - pub COLLAPSIBLE_MATCH, - style, - "Nested `match` or `if let` expressions where the patterns may be \"collapsed\" together." -} - -declare_lint_pass!(CollapsibleMatch => [COLLAPSIBLE_MATCH]); +use super::COLLAPSIBLE_MATCH; -impl<'tcx> LateLintPass<'tcx> for CollapsibleMatch { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { - 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)); - } - } - }, - Some(IfLetOrMatch::IfLet(_, pat, body, els)) => { - check_arm(cx, false, pat, body, None, els); - }, - None => {}, +pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { + 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)); } } } +pub(super) fn check_if_let<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + body: &'tcx Expr<'_>, + else_expr: Option<&'tcx Expr<'_>>, +) { + check_arm(cx, false, pat, body, None, else_expr); +} + fn check_arm<'tcx>( cx: &LateContext<'tcx>, outer_is_match: bool, diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 2e1f7646eb400..a68eec842abc5 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_wild; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{higher, is_wild}; use rustc_ast::{Attribute, LitKind}; use rustc_errors::Applicability; use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Guard, Pat}; @@ -11,22 +11,24 @@ use rustc_span::source_map::Spanned; use super::MATCH_LIKE_MATCHES_MACRO; /// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!` -pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let Some(higher::IfLet { - let_pat, +pub(crate) fn check_if_let<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + let_pat: &'tcx Pat<'_>, + let_expr: &'tcx Expr<'_>, + then_expr: &'tcx Expr<'_>, + else_expr: &'tcx Expr<'_>, +) { + find_matches_sugg( + cx, let_expr, - if_then, - if_else: Some(if_else), - }) = higher::IfLet::hir(cx, expr) - { - find_matches_sugg( - cx, - let_expr, - IntoIterator::into_iter([(&[][..], Some(let_pat), if_then, None), (&[][..], None, if_else, None)]), - expr, - true, - ); - } + IntoIterator::into_iter([ + (&[][..], Some(let_pat), then_expr, None), + (&[][..], None, else_expr, None), + ]), + expr, + true, + ); } pub(super) fn check_match<'tcx>( diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index 3d8391bce2b28..4a77fb8c31bbd 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -1,12 +1,14 @@ use clippy_utils::source::{snippet_opt, span_starts_with, walk_span_to_context}; -use clippy_utils::{meets_msrv, msrvs}; +use clippy_utils::{higher, meets_msrv, msrvs}; use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat}; use rustc_lexer::{tokenize, TokenKind}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{Span, SpanData, SyntaxContext}; +mod collapsible_match; mod infallible_destructuring_match; mod match_as_ref; mod match_bool; @@ -610,6 +612,44 @@ declare_clippy_lint! { "`match` or match-like `if let` that are unnecessary" } +declare_clippy_lint! { + /// ### What it does + /// Finds nested `match` or `if let` expressions where the patterns may be "collapsed" together + /// without adding any branches. + /// + /// Note that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only + /// cases where merging would most likely make the code more readable. + /// + /// ### Why is this bad? + /// It is unnecessarily verbose and complex. + /// + /// ### Example + /// ```rust + /// fn func(opt: Option>) { + /// let n = match opt { + /// Some(n) => match n { + /// Ok(n) => n, + /// _ => return, + /// } + /// None => return, + /// }; + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn func(opt: Option>) { + /// let n = match opt { + /// Some(Ok(n)) => n, + /// _ => return, + /// }; + /// } + /// ``` + #[clippy::version = "1.50.0"] + pub COLLAPSIBLE_MATCH, + style, + "Nested `match` or `if let` expressions where the patterns may be \"collapsed\" together." +} + #[derive(Default)] pub struct Matches { msrv: Option, @@ -644,19 +684,29 @@ impl_lint_pass!(Matches => [ MATCH_LIKE_MATCHES_MACRO, MATCH_SAME_ARMS, NEEDLESS_MATCH, + COLLAPSIBLE_MATCH, ]); impl<'tcx> LateLintPass<'tcx> for Matches { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if expr.span.from_expansion() { + if in_external_macro(cx.sess(), expr.span) { return; } + let from_expansion = expr.span.from_expansion(); if let ExprKind::Match(ex, arms, source) = expr.kind { if !span_starts_with(cx, expr.span, "match") { return; } - if !contains_cfg_arm(cx, expr, ex, arms) { + + collapsible_match::check_match(cx, arms); + if !from_expansion { + // These don't depend on a relationship between multiple arms + match_wild_err_arm::check(cx, ex, arms); + wild_in_or_pats::check(cx, arms); + } + + if !from_expansion && !contains_cfg_arm(cx, expr, ex, arms) { if source == MatchSource::Normal { if !(meets_msrv(self.msrv, msrvs::MATCHES_MACRO) && match_like_matches::check_match(cx, expr, ex, arms)) @@ -680,16 +730,32 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } match_ref_pats::check(cx, ex, arms.iter().map(|el| el.pat), expr); } - - // These don't depend on a relationship between multiple arms - match_wild_err_arm::check(cx, ex, arms); - wild_in_or_pats::check(cx, arms); - } else { - if meets_msrv(self.msrv, msrvs::MATCHES_MACRO) { - match_like_matches::check(cx, expr); + } else if let Some(if_let) = higher::IfLet::hir(cx, expr) { + collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else); + if !from_expansion { + if let Some(else_expr) = if_let.if_else { + if meets_msrv(self.msrv, msrvs::MATCHES_MACRO) { + match_like_matches::check_if_let( + cx, + expr, + if_let.let_pat, + if_let.let_expr, + if_let.if_then, + else_expr, + ); + } + } + redundant_pattern_match::check_if_let( + cx, + expr, + if_let.let_pat, + if_let.let_expr, + if_let.if_else.is_some(), + ); + needless_match::check_if_let(cx, expr, &if_let); } + } else if !from_expansion { redundant_pattern_match::check(cx, expr); - needless_match::check(cx, expr); } } diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index f920ad4651f9d..fa19cddd35ec7 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -47,20 +47,18 @@ pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], /// some_enum /// } /// ``` -pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>) { - if let Some(ref if_let) = higher::IfLet::hir(cx, ex) { - if !is_else_clause(cx.tcx, ex) && expr_ty_matches_p_ty(cx, if_let.let_expr, ex) && check_if_let(cx, if_let) { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - NEEDLESS_MATCH, - ex.span, - "this if-let expression is unnecessary", - "replace it with", - snippet_with_applicability(cx, if_let.let_expr.span, "..", &mut applicability).to_string(), - applicability, - ); - } +pub(crate) fn check_if_let<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'_>, if_let: &higher::IfLet<'tcx>) { + if !is_else_clause(cx.tcx, ex) && expr_ty_matches_p_ty(cx, if_let.let_expr, ex) && check_if_let_inner(cx, if_let) { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + NEEDLESS_MATCH, + ex.span, + "this if-let expression is unnecessary", + "replace it with", + snippet_with_applicability(cx, if_let.let_expr.span, "..", &mut applicability).to_string(), + applicability, + ); } } @@ -77,7 +75,7 @@ fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>]) true } -fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool { +fn check_if_let_inner(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool { if let Some(if_else) = if_let.if_else { if !pat_same_as_expr(if_let.let_pat, peel_blocks_with_stmt(if_let.if_then)) { return false; @@ -85,7 +83,7 @@ fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool { // Recursively check for each `else if let` phrase, if let Some(ref nested_if_let) = higher::IfLet::hir(cx, if_else) { - return check_if_let(cx, nested_if_let); + return check_if_let_inner(cx, nested_if_let); } if matches!(if_else.kind, ExprKind::Block(..)) { diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index 1a8b9d15f370f..095cd43ea13fb 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -18,19 +18,21 @@ use rustc_middle::ty::{self, subst::GenericArgKind, DefIdTree, Ty}; use rustc_span::sym; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let Some(higher::IfLet { - if_else, - let_pat, - let_expr, - .. - }) = higher::IfLet::hir(cx, expr) - { - find_sugg_for_if_let(cx, expr, let_pat, let_expr, "if", if_else.is_some()); - } else if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) { + 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); } } +pub(super) fn check_if_let<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + pat: &'tcx Pat<'_>, + scrutinee: &'tcx Expr<'_>, + has_else: bool, +) { + find_sugg_for_if_let(cx, expr, pat, scrutinee, "if", has_else); +} + // Extract the generic arguments out of a type fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option> { if_chain! { From b337f9e62e8e8adee47684757c1ad101067beae2 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 3 Jun 2022 12:29:54 -0400 Subject: [PATCH 70/79] Merge `ManualUnwrapOr` into `Matches` lint pass --- clippy_lints/src/lib.register_all.rs | 2 +- clippy_lints/src/lib.register_complexity.rs | 2 +- clippy_lints/src/lib.register_lints.rs | 2 +- clippy_lints/src/lib.rs | 2 - clippy_lints/src/manual_unwrap_or.rs | 123 ------------------- clippy_lints/src/matches/manual_unwrap_or.rs | 83 +++++++++++++ clippy_lints/src/matches/mod.rs | 35 +++++- 7 files changed, 120 insertions(+), 129 deletions(-) delete mode 100644 clippy_lints/src/manual_unwrap_or.rs create mode 100644 clippy_lints/src/matches/manual_unwrap_or.rs diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index b1da36a0d4017..46af1d8fb09c3 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -136,7 +136,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(manual_map::MANUAL_MAP), LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(manual_strip::MANUAL_STRIP), - LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR), LintId::of(map_clone::MAP_CLONE), LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), @@ -144,6 +143,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(match_str_case_mismatch::MATCH_STR_CASE_MISMATCH), LintId::of(matches::COLLAPSIBLE_MATCH), LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), + LintId::of(matches::MANUAL_UNWRAP_OR), LintId::of(matches::MATCH_AS_REF), LintId::of(matches::MATCH_LIKE_MATCHES_MACRO), LintId::of(matches::MATCH_OVERLAPPING_ARM), diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs index 29b38a01b7e2f..4f1c3673f853c 100644 --- a/clippy_lints/src/lib.register_complexity.rs +++ b/clippy_lints/src/lib.register_complexity.rs @@ -25,9 +25,9 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(loops::SINGLE_ELEMENT_LOOP), LintId::of(loops::WHILE_LET_LOOP), LintId::of(manual_strip::MANUAL_STRIP), - LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR), LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), + LintId::of(matches::MANUAL_UNWRAP_OR), LintId::of(matches::MATCH_AS_REF), LintId::of(matches::MATCH_SINGLE_BINDING), LintId::of(matches::NEEDLESS_MATCH), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 58e18b710ea88..cb52000fa55a2 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -254,7 +254,6 @@ store.register_lints(&[ manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, manual_ok_or::MANUAL_OK_OR, manual_strip::MANUAL_STRIP, - manual_unwrap_or::MANUAL_UNWRAP_OR, map_clone::MAP_CLONE, map_err_ignore::MAP_ERR_IGNORE, map_unit_fn::OPTION_MAP_UNIT_FN, @@ -264,6 +263,7 @@ store.register_lints(&[ match_str_case_mismatch::MATCH_STR_CASE_MISMATCH, matches::COLLAPSIBLE_MATCH, matches::INFALLIBLE_DESTRUCTURING_MATCH, + matches::MANUAL_UNWRAP_OR, matches::MATCH_AS_REF, matches::MATCH_BOOL, matches::MATCH_LIKE_MATCHES_MACRO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6e16d470ac878..3fdd0db818790 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -283,7 +283,6 @@ mod manual_map; mod manual_non_exhaustive; mod manual_ok_or; mod manual_strip; -mod manual_unwrap_or; mod map_clone; mod map_err_ignore; mod map_unit_fn; @@ -834,7 +833,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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)); diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs deleted file mode 100644 index b3a91d9f18f5d..0000000000000 --- a/clippy_lints/src/manual_unwrap_or.rs +++ /dev/null @@ -1,123 +0,0 @@ -use clippy_utils::consts::constant_simple; -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::usage::contains_return_break_continue_macro; -use clippy_utils::{in_constant, is_lang_ctor, path_to_local_id, sugg}; -use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; -use rustc_hir::{Arm, Expr, ExprKind, PatKind}; -use rustc_lint::LintContext; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; - -declare_clippy_lint! { - /// ### What it does - /// Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`. - /// - /// ### Why is this bad? - /// Concise code helps focusing on behavior instead of boilerplate. - /// - /// ### Example - /// ```rust - /// let foo: Option = None; - /// match foo { - /// Some(v) => v, - /// None => 1, - /// }; - /// ``` - /// - /// Use instead: - /// ```rust - /// let foo: Option = None; - /// foo.unwrap_or(1); - /// ``` - #[clippy::version = "1.49.0"] - pub MANUAL_UNWRAP_OR, - complexity, - "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`" -} - -declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]); - -impl<'tcx> LateLintPass<'tcx> for ManualUnwrapOr { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) { - return; - } - lint_manual_unwrap_or(cx, expr); - } -} - -fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - fn applicable_or_arm<'a>(cx: &LateContext<'_>, arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> { - if_chain! { - if arms.len() == 2; - if arms.iter().all(|arm| arm.guard.is_none()); - if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| { - match arm.pat.kind { - PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), - PatKind::TupleStruct(ref qpath, [pat], _) => - matches!(pat.kind, PatKind::Wild) && is_lang_ctor(cx, qpath, ResultErr), - _ => false, - } - }); - let unwrap_arm = &arms[1 - idx]; - if let PatKind::TupleStruct(ref qpath, [unwrap_pat], _) = unwrap_arm.pat.kind; - if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk); - if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind; - if path_to_local_id(unwrap_arm.body, binding_hir_id); - if cx.typeck_results().expr_adjustments(unwrap_arm.body).is_empty(); - if !contains_return_break_continue_macro(or_arm.body); - then { - Some(or_arm) - } else { - None - } - } - } - - if_chain! { - if let ExprKind::Match(scrutinee, match_arms, _) = expr.kind; - let ty = cx.typeck_results().expr_ty(scrutinee); - if let Some(ty_name) = if is_type_diagnostic_item(cx, ty, sym::Option) { - Some("Option") - } else if is_type_diagnostic_item(cx, ty, sym::Result) { - Some("Result") - } else { - None - }; - if let Some(or_arm) = applicable_or_arm(cx, match_arms); - if let Some(or_body_snippet) = snippet_opt(cx, or_arm.body.span); - if let Some(indent) = indent_of(cx, expr.span); - if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some(); - then { - let reindented_or_body = - reindent_multiline(or_body_snippet.into(), true, Some(indent)); - - let suggestion = if scrutinee.span.from_expansion() { - // we don't want parentheses around macro, e.g. `(some_macro!()).unwrap_or(0)` - sugg::Sugg::hir_with_macro_callsite(cx, scrutinee, "..") - } - else { - sugg::Sugg::hir(cx, scrutinee, "..").maybe_par() - }; - - span_lint_and_sugg( - cx, - MANUAL_UNWRAP_OR, expr.span, - &format!("this pattern reimplements `{}::unwrap_or`", ty_name), - "replace with", - format!( - "{}.unwrap_or({})", - suggestion, - reindented_or_body, - ), - Applicability::MachineApplicable, - ); - } - } -} diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs new file mode 100644 index 0000000000000..e1111c80f2fe2 --- /dev/null +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -0,0 +1,83 @@ +use clippy_utils::consts::constant_simple; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::usage::contains_return_break_continue_macro; +use clippy_utils::{is_lang_ctor, path_to_local_id, sugg}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; +use rustc_hir::{Arm, Expr, PatKind}; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::MANUAL_UNWRAP_OR; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, scrutinee: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) { + let ty = cx.typeck_results().expr_ty(scrutinee); + if_chain! { + if let Some(ty_name) = if is_type_diagnostic_item(cx, ty, sym::Option) { + Some("Option") + } else if is_type_diagnostic_item(cx, ty, sym::Result) { + Some("Result") + } else { + None + }; + if let Some(or_arm) = applicable_or_arm(cx, arms); + if let Some(or_body_snippet) = snippet_opt(cx, or_arm.body.span); + if let Some(indent) = indent_of(cx, expr.span); + if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some(); + then { + let reindented_or_body = + reindent_multiline(or_body_snippet.into(), true, Some(indent)); + + let suggestion = if scrutinee.span.from_expansion() { + // we don't want parentheses around macro, e.g. `(some_macro!()).unwrap_or(0)` + sugg::Sugg::hir_with_macro_callsite(cx, scrutinee, "..") + } + else { + sugg::Sugg::hir(cx, scrutinee, "..").maybe_par() + }; + + span_lint_and_sugg( + cx, + MANUAL_UNWRAP_OR, expr.span, + &format!("this pattern reimplements `{}::unwrap_or`", ty_name), + "replace with", + format!( + "{}.unwrap_or({})", + suggestion, + reindented_or_body, + ), + Applicability::MachineApplicable, + ); + } + } +} + +fn applicable_or_arm<'a>(cx: &LateContext<'_>, arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> { + if_chain! { + if arms.len() == 2; + if arms.iter().all(|arm| arm.guard.is_none()); + if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| { + match arm.pat.kind { + PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), + PatKind::TupleStruct(ref qpath, [pat], _) => + matches!(pat.kind, PatKind::Wild) && is_lang_ctor(cx, qpath, ResultErr), + _ => false, + } + }); + let unwrap_arm = &arms[1 - idx]; + if let PatKind::TupleStruct(ref qpath, [unwrap_pat], _) = unwrap_arm.pat.kind; + if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk); + if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind; + if path_to_local_id(unwrap_arm.body, binding_hir_id); + if cx.typeck_results().expr_adjustments(unwrap_arm.body).is_empty(); + if !contains_return_break_continue_macro(or_arm.body); + then { + Some(or_arm) + } else { + None + } + } +} diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index 4a77fb8c31bbd..ade47b1ed88e3 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -1,5 +1,5 @@ use clippy_utils::source::{snippet_opt, span_starts_with, walk_span_to_context}; -use clippy_utils::{higher, meets_msrv, msrvs}; +use clippy_utils::{higher, in_constant, meets_msrv, msrvs}; use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat}; use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -10,6 +10,7 @@ use rustc_span::{Span, SpanData, SyntaxContext}; mod collapsible_match; mod infallible_destructuring_match; +mod manual_unwrap_or; mod match_as_ref; mod match_bool; mod match_like_matches; @@ -650,6 +651,33 @@ declare_clippy_lint! { "Nested `match` or `if let` expressions where the patterns may be \"collapsed\" together." } +declare_clippy_lint! { + /// ### What it does + /// Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`. + /// + /// ### Why is this bad? + /// Concise code helps focusing on behavior instead of boilerplate. + /// + /// ### Example + /// ```rust + /// let foo: Option = None; + /// match foo { + /// Some(v) => v, + /// None => 1, + /// }; + /// ``` + /// + /// Use instead: + /// ```rust + /// let foo: Option = None; + /// foo.unwrap_or(1); + /// ``` + #[clippy::version = "1.49.0"] + pub MANUAL_UNWRAP_OR, + complexity, + "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`" +} + #[derive(Default)] pub struct Matches { msrv: Option, @@ -685,6 +713,7 @@ impl_lint_pass!(Matches => [ MATCH_SAME_ARMS, NEEDLESS_MATCH, COLLAPSIBLE_MATCH, + MANUAL_UNWRAP_OR, ]); impl<'tcx> LateLintPass<'tcx> for Matches { @@ -722,6 +751,10 @@ impl<'tcx> LateLintPass<'tcx> for Matches { match_as_ref::check(cx, ex, arms, expr); needless_match::check_match(cx, ex, arms, expr); + if !in_constant(cx, expr.hir_id) { + manual_unwrap_or::check(cx, expr, ex, arms); + } + if self.infallible_destructuring_match_linted { self.infallible_destructuring_match_linted = false; } else { From 3d8d7341509e4868c91fa7fe24703e69c21a9869 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 3 Jun 2022 13:10:57 -0400 Subject: [PATCH 71/79] Move `MatchOnVecItems` into `Matches` lint pass --- clippy_lints/src/lib.register_lints.rs | 2 +- clippy_lints/src/lib.register_pedantic.rs | 2 +- clippy_lints/src/lib.rs | 2 - clippy_lints/src/match_on_vec_items.rs | 104 ------------------ .../src/matches/match_on_vec_items.rs | 61 ++++++++++ clippy_lints/src/matches/mod.rs | 40 +++++++ 6 files changed, 103 insertions(+), 108 deletions(-) delete mode 100644 clippy_lints/src/match_on_vec_items.rs create mode 100644 clippy_lints/src/matches/match_on_vec_items.rs diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index cb52000fa55a2..fb44df57de676 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -258,7 +258,6 @@ store.register_lints(&[ map_err_ignore::MAP_ERR_IGNORE, map_unit_fn::OPTION_MAP_UNIT_FN, map_unit_fn::RESULT_MAP_UNIT_FN, - match_on_vec_items::MATCH_ON_VEC_ITEMS, match_result_ok::MATCH_RESULT_OK, match_str_case_mismatch::MATCH_STR_CASE_MISMATCH, matches::COLLAPSIBLE_MATCH, @@ -267,6 +266,7 @@ store.register_lints(&[ matches::MATCH_AS_REF, matches::MATCH_BOOL, matches::MATCH_LIKE_MATCHES_MACRO, + matches::MATCH_ON_VEC_ITEMS, matches::MATCH_OVERLAPPING_ARM, matches::MATCH_REF_PATS, matches::MATCH_SAME_ARMS, diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs index 2e47a287d5cf6..a8b6c5a5d63fe 100644 --- a/clippy_lints/src/lib.register_pedantic.rs +++ b/clippy_lints/src/lib.register_pedantic.rs @@ -51,8 +51,8 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(macro_use::MACRO_USE_IMPORTS), LintId::of(manual_assert::MANUAL_ASSERT), LintId::of(manual_ok_or::MANUAL_OK_OR), - LintId::of(match_on_vec_items::MATCH_ON_VEC_ITEMS), LintId::of(matches::MATCH_BOOL), + LintId::of(matches::MATCH_ON_VEC_ITEMS), LintId::of(matches::MATCH_SAME_ARMS), LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS), LintId::of(matches::MATCH_WILD_ERR_ARM), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3fdd0db818790..50b67918fc00d 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -286,7 +286,6 @@ mod manual_strip; mod map_clone; mod map_err_ignore; mod map_unit_fn; -mod match_on_vec_items; mod match_result_ok; mod match_str_case_mismatch; mod matches; @@ -815,7 +814,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(if_not_else::IfNotElse)); store.register_late_pass(|| Box::new(equatable_if_let::PatternEquality)); store.register_late_pass(|| Box::new(mut_mutex_lock::MutMutexLock)); - store.register_late_pass(|| Box::new(match_on_vec_items::MatchOnVecItems)); store.register_late_pass(|| Box::new(manual_async_fn::ManualAsyncFn)); store.register_late_pass(|| Box::new(vec_resize_to_zero::VecResizeToZero)); store.register_late_pass(|| Box::new(panic_in_result_fn::PanicInResultFn)); diff --git a/clippy_lints/src/match_on_vec_items.rs b/clippy_lints/src/match_on_vec_items.rs deleted file mode 100644 index 583b577ffe25d..0000000000000 --- a/clippy_lints/src/match_on_vec_items.rs +++ /dev/null @@ -1,104 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet; -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; -use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, LangItem, MatchSource}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; - -declare_clippy_lint! { - /// ### What it does - /// Checks for `match vec[idx]` or `match vec[n..m]`. - /// - /// ### Why is this bad? - /// This can panic at runtime. - /// - /// ### Example - /// ```rust, no_run - /// let arr = vec![0, 1, 2, 3]; - /// let idx = 1; - /// - /// // Bad - /// match arr[idx] { - /// 0 => println!("{}", 0), - /// 1 => println!("{}", 3), - /// _ => {}, - /// } - /// ``` - /// Use instead: - /// ```rust, no_run - /// let arr = vec![0, 1, 2, 3]; - /// let idx = 1; - /// - /// // Good - /// match arr.get(idx) { - /// Some(0) => println!("{}", 0), - /// Some(1) => println!("{}", 3), - /// _ => {}, - /// } - /// ``` - #[clippy::version = "1.45.0"] - pub MATCH_ON_VEC_ITEMS, - pedantic, - "matching on vector elements can panic" -} - -declare_lint_pass!(MatchOnVecItems => [MATCH_ON_VEC_ITEMS]); - -impl<'tcx> LateLintPass<'tcx> for MatchOnVecItems { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if_chain! { - if !in_external_macro(cx.sess(), expr.span); - if let ExprKind::Match(match_expr, _, MatchSource::Normal) = expr.kind; - if let Some(idx_expr) = is_vec_indexing(cx, match_expr); - if let ExprKind::Index(vec, idx) = idx_expr.kind; - - then { - // FIXME: could be improved to suggest surrounding every pattern with Some(_), - // but only when `or_patterns` are stabilized. - span_lint_and_sugg( - cx, - MATCH_ON_VEC_ITEMS, - match_expr.span, - "indexing into a vector may panic", - "try this", - format!( - "{}.get({})", - snippet(cx, vec.span, ".."), - snippet(cx, idx.span, "..") - ), - Applicability::MaybeIncorrect - ); - } - } - } -} - -fn is_vec_indexing<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { - if_chain! { - if let ExprKind::Index(array, index) = expr.kind; - if is_vector(cx, array); - if !is_full_range(cx, index); - - then { - return Some(expr); - } - } - - None -} - -fn is_vector(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - let ty = cx.typeck_results().expr_ty(expr); - let ty = ty.peel_refs(); - is_type_diagnostic_item(cx, ty, sym::Vec) -} - -fn is_full_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - let ty = cx.typeck_results().expr_ty(expr); - let ty = ty.peel_refs(); - is_type_lang_item(cx, ty, LangItem::RangeFull) -} diff --git a/clippy_lints/src/matches/match_on_vec_items.rs b/clippy_lints/src/matches/match_on_vec_items.rs new file mode 100644 index 0000000000000..2917f85c45f53 --- /dev/null +++ b/clippy_lints/src/matches/match_on_vec_items.rs @@ -0,0 +1,61 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, LangItem}; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::MATCH_ON_VEC_ITEMS; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>) { + if_chain! { + if let Some(idx_expr) = is_vec_indexing(cx, scrutinee); + if let ExprKind::Index(vec, idx) = idx_expr.kind; + + then { + // FIXME: could be improved to suggest surrounding every pattern with Some(_), + // but only when `or_patterns` are stabilized. + span_lint_and_sugg( + cx, + MATCH_ON_VEC_ITEMS, + scrutinee.span, + "indexing into a vector may panic", + "try this", + format!( + "{}.get({})", + snippet(cx, vec.span, ".."), + snippet(cx, idx.span, "..") + ), + Applicability::MaybeIncorrect + ); + } + } +} + +fn is_vec_indexing<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { + if_chain! { + if let ExprKind::Index(array, index) = expr.kind; + if is_vector(cx, array); + if !is_full_range(cx, index); + + then { + return Some(expr); + } + } + + None +} + +fn is_vector(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let ty = cx.typeck_results().expr_ty(expr); + let ty = ty.peel_refs(); + is_type_diagnostic_item(cx, ty, sym::Vec) +} + +fn is_full_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let ty = cx.typeck_results().expr_ty(expr); + let ty = ty.peel_refs(); + is_type_lang_item(cx, ty, LangItem::RangeFull) +} diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index ade47b1ed88e3..7fb449028e7a4 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -14,6 +14,7 @@ mod manual_unwrap_or; mod match_as_ref; mod match_bool; mod match_like_matches; +mod match_on_vec_items; mod match_ref_pats; mod match_same_arms; mod match_single_binding; @@ -678,6 +679,43 @@ declare_clippy_lint! { "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for `match vec[idx]` or `match vec[n..m]`. + /// + /// ### Why is this bad? + /// This can panic at runtime. + /// + /// ### Example + /// ```rust, no_run + /// let arr = vec![0, 1, 2, 3]; + /// let idx = 1; + /// + /// // Bad + /// match arr[idx] { + /// 0 => println!("{}", 0), + /// 1 => println!("{}", 3), + /// _ => {}, + /// } + /// ``` + /// Use instead: + /// ```rust, no_run + /// let arr = vec![0, 1, 2, 3]; + /// let idx = 1; + /// + /// // Good + /// match arr.get(idx) { + /// Some(0) => println!("{}", 0), + /// Some(1) => println!("{}", 3), + /// _ => {}, + /// } + /// ``` + #[clippy::version = "1.45.0"] + pub MATCH_ON_VEC_ITEMS, + pedantic, + "matching on vector elements can panic" +} + #[derive(Default)] pub struct Matches { msrv: Option, @@ -714,6 +752,7 @@ impl_lint_pass!(Matches => [ NEEDLESS_MATCH, COLLAPSIBLE_MATCH, MANUAL_UNWRAP_OR, + MATCH_ON_VEC_ITEMS, ]); impl<'tcx> LateLintPass<'tcx> for Matches { @@ -750,6 +789,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { match_wild_enum::check(cx, ex, arms); match_as_ref::check(cx, ex, arms, expr); needless_match::check_match(cx, ex, arms, expr); + match_on_vec_items::check(cx, ex); if !in_constant(cx, expr.hir_id) { manual_unwrap_or::check(cx, expr, ex, arms); From 8c8a52eeeb9ca47575200eacd1ab86bf46e8dc15 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 3 Jun 2022 13:16:02 -0400 Subject: [PATCH 72/79] Move `MatchStrCaseMismatch` into `Matches` lint pass --- clippy_lints/src/lib.register_all.rs | 2 +- clippy_lints/src/lib.register_correctness.rs | 2 +- clippy_lints/src/lib.register_lints.rs | 2 +- clippy_lints/src/lib.rs | 2 - .../{ => matches}/match_str_case_mismatch.rs | 73 +++++-------------- clippy_lints/src/matches/mod.rs | 34 +++++++++ 6 files changed, 55 insertions(+), 60 deletions(-) rename clippy_lints/src/{ => matches}/match_str_case_mismatch.rs (64%) diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 46af1d8fb09c3..15e744229a1cb 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -140,7 +140,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), LintId::of(match_result_ok::MATCH_RESULT_OK), - LintId::of(match_str_case_mismatch::MATCH_STR_CASE_MISMATCH), LintId::of(matches::COLLAPSIBLE_MATCH), LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), LintId::of(matches::MANUAL_UNWRAP_OR), @@ -149,6 +148,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(matches::MATCH_OVERLAPPING_ARM), LintId::of(matches::MATCH_REF_PATS), LintId::of(matches::MATCH_SINGLE_BINDING), + LintId::of(matches::MATCH_STR_CASE_MISMATCH), LintId::of(matches::NEEDLESS_MATCH), LintId::of(matches::REDUNDANT_PATTERN_MATCHING), LintId::of(matches::SINGLE_MATCH), diff --git a/clippy_lints/src/lib.register_correctness.rs b/clippy_lints/src/lib.register_correctness.rs index 6bf2c4bbaedc0..50cdd0af92305 100644 --- a/clippy_lints/src/lib.register_correctness.rs +++ b/clippy_lints/src/lib.register_correctness.rs @@ -39,7 +39,7 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve LintId::of(loops::ITER_NEXT_LOOP), LintId::of(loops::NEVER_LOOP), LintId::of(loops::WHILE_IMMUTABLE_CONDITION), - LintId::of(match_str_case_mismatch::MATCH_STR_CASE_MISMATCH), + LintId::of(matches::MATCH_STR_CASE_MISMATCH), LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT), LintId::of(methods::CLONE_DOUBLE_REF), LintId::of(methods::ITERATOR_STEP_BY_ZERO), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index fb44df57de676..59ba295a88732 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -259,7 +259,6 @@ store.register_lints(&[ map_unit_fn::OPTION_MAP_UNIT_FN, map_unit_fn::RESULT_MAP_UNIT_FN, match_result_ok::MATCH_RESULT_OK, - match_str_case_mismatch::MATCH_STR_CASE_MISMATCH, matches::COLLAPSIBLE_MATCH, matches::INFALLIBLE_DESTRUCTURING_MATCH, matches::MANUAL_UNWRAP_OR, @@ -271,6 +270,7 @@ store.register_lints(&[ matches::MATCH_REF_PATS, matches::MATCH_SAME_ARMS, matches::MATCH_SINGLE_BINDING, + matches::MATCH_STR_CASE_MISMATCH, matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS, matches::MATCH_WILD_ERR_ARM, matches::NEEDLESS_MATCH, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 50b67918fc00d..7335b4a35c8c6 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -287,7 +287,6 @@ mod map_clone; mod map_err_ignore; mod map_unit_fn; mod match_result_ok; -mod match_str_case_mismatch; mod matches; mod mem_forget; mod mem_replace; @@ -875,7 +874,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: )) }); store.register_late_pass(move || Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks)); - store.register_late_pass(|| Box::new(match_str_case_mismatch::MatchStrCaseMismatch)); store.register_late_pass(move || Box::new(format_args::FormatArgs)); store.register_late_pass(|| Box::new(trailing_empty_array::TrailingEmptyArray)); store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes)); diff --git a/clippy_lints/src/match_str_case_mismatch.rs b/clippy_lints/src/matches/match_str_case_mismatch.rs similarity index 64% rename from clippy_lints/src/match_str_case_mismatch.rs rename to clippy_lints/src/matches/match_str_case_mismatch.rs index d97a878825af2..8302ce426e570 100644 --- a/clippy_lints/src/match_str_case_mismatch.rs +++ b/clippy_lints/src/matches/match_str_case_mismatch.rs @@ -3,46 +3,13 @@ use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::lint::in_external_macro; +use rustc_hir::{Arm, Expr, ExprKind, PatKind}; +use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Symbol; use rustc_span::{sym, Span}; -declare_clippy_lint! { - /// ### What it does - /// Checks for `match` expressions modifying the case of a string with non-compliant arms - /// - /// ### Why is this bad? - /// The arm is unreachable, which is likely a mistake - /// - /// ### Example - /// ```rust - /// # let text = "Foo"; - /// match &*text.to_ascii_lowercase() { - /// "foo" => {}, - /// "Bar" => {}, - /// _ => {}, - /// } - /// ``` - /// Use instead: - /// ```rust - /// # let text = "Foo"; - /// match &*text.to_ascii_lowercase() { - /// "foo" => {}, - /// "bar" => {}, - /// _ => {}, - /// } - /// ``` - #[clippy::version = "1.58.0"] - pub MATCH_STR_CASE_MISMATCH, - correctness, - "creation of a case altering match expression with non-compliant arms" -} - -declare_lint_pass!(MatchStrCaseMismatch => [MATCH_STR_CASE_MISMATCH]); +use super::MATCH_STR_CASE_MISMATCH; #[derive(Debug)] enum CaseMethod { @@ -52,25 +19,21 @@ enum CaseMethod { AsciiUppercase, } -impl<'tcx> LateLintPass<'tcx> for MatchStrCaseMismatch { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if !in_external_macro(cx.tcx.sess, expr.span); - if let ExprKind::Match(match_expr, arms, MatchSource::Normal) = expr.kind; - if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(match_expr).kind(); - if let ty::Str = ty.kind(); - then { - let mut visitor = MatchExprVisitor { - cx, - case_method: None, - }; - - visitor.visit_expr(match_expr); - - if let Some(case_method) = visitor.case_method { - if let Some((bad_case_span, bad_case_sym)) = verify_case(&case_method, arms) { - lint(cx, &case_method, bad_case_span, bad_case_sym.as_str()); - } +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) { + if_chain! { + if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(scrutinee).kind(); + if let ty::Str = ty.kind(); + then { + let mut visitor = MatchExprVisitor { + cx, + case_method: None, + }; + + visitor.visit_expr(scrutinee); + + if let Some(case_method) = visitor.case_method { + if let Some((bad_case_span, bad_case_sym)) = verify_case(&case_method, arms) { + lint(cx, &case_method, bad_case_span, bad_case_sym.as_str()); } } } diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index 7fb449028e7a4..a58c71ca5f808 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -18,6 +18,7 @@ mod match_on_vec_items; mod match_ref_pats; mod match_same_arms; mod match_single_binding; +mod match_str_case_mismatch; mod match_wild_enum; mod match_wild_err_arm; mod needless_match; @@ -716,6 +717,37 @@ declare_clippy_lint! { "matching on vector elements can panic" } +declare_clippy_lint! { + /// ### What it does + /// Checks for `match` expressions modifying the case of a string with non-compliant arms + /// + /// ### Why is this bad? + /// The arm is unreachable, which is likely a mistake + /// + /// ### Example + /// ```rust + /// # let text = "Foo"; + /// match &*text.to_ascii_lowercase() { + /// "foo" => {}, + /// "Bar" => {}, + /// _ => {}, + /// } + /// ``` + /// Use instead: + /// ```rust + /// # let text = "Foo"; + /// match &*text.to_ascii_lowercase() { + /// "foo" => {}, + /// "bar" => {}, + /// _ => {}, + /// } + /// ``` + #[clippy::version = "1.58.0"] + pub MATCH_STR_CASE_MISMATCH, + correctness, + "creation of a case altering match expression with non-compliant arms" +} + #[derive(Default)] pub struct Matches { msrv: Option, @@ -753,6 +785,7 @@ impl_lint_pass!(Matches => [ COLLAPSIBLE_MATCH, MANUAL_UNWRAP_OR, MATCH_ON_VEC_ITEMS, + MATCH_STR_CASE_MISMATCH, ]); impl<'tcx> LateLintPass<'tcx> for Matches { @@ -790,6 +823,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { match_as_ref::check(cx, ex, arms, expr); needless_match::check_match(cx, ex, arms, expr); match_on_vec_items::check(cx, ex); + match_str_case_mismatch::check(cx, ex, arms); if !in_constant(cx, expr.hir_id) { manual_unwrap_or::check(cx, expr, ex, arms); From dbc7753fb2b5e1af5c00f3c1e246cfd594611c75 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 3 Jun 2022 13:35:10 -0400 Subject: [PATCH 73/79] Merge `SignificantDropInScrutinee` into `Matches` lint pass --- clippy_lints/src/lib.register_all.rs | 2 +- clippy_lints/src/lib.register_lints.rs | 2 +- clippy_lints/src/lib.register_suspicious.rs | 2 +- clippy_lints/src/lib.rs | 2 - clippy_lints/src/matches/mod.rs | 83 ++++++++++- .../significant_drop_in_scrutinee.rs | 132 ++++-------------- 6 files changed, 109 insertions(+), 114 deletions(-) rename clippy_lints/src/{ => matches}/significant_drop_in_scrutinee.rs (75%) diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 15e744229a1cb..435c0829a6770 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -151,6 +151,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(matches::MATCH_STR_CASE_MISMATCH), LintId::of(matches::NEEDLESS_MATCH), LintId::of(matches::REDUNDANT_PATTERN_MATCHING), + LintId::of(matches::SIGNIFICANT_DROP_IN_SCRUTINEE), LintId::of(matches::SINGLE_MATCH), LintId::of(matches::WILDCARD_IN_OR_PATTERNS), LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE), @@ -282,7 +283,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(self_assignment::SELF_ASSIGNMENT), LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS), LintId::of(serde_api::SERDE_API_MISUSE), - LintId::of(significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE), LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 59ba295a88732..7fc6ff8a2fbe4 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -276,6 +276,7 @@ store.register_lints(&[ matches::NEEDLESS_MATCH, matches::REDUNDANT_PATTERN_MATCHING, matches::REST_PAT_IN_FULLY_BOUND_STRUCTS, + matches::SIGNIFICANT_DROP_IN_SCRUTINEE, matches::SINGLE_MATCH, matches::SINGLE_MATCH_ELSE, matches::WILDCARD_ENUM_MATCH_ARM, @@ -479,7 +480,6 @@ store.register_lints(&[ shadow::SHADOW_REUSE, shadow::SHADOW_SAME, shadow::SHADOW_UNRELATED, - significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE, single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES, single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs index 43ced5070f17e..7b13713c36e59 100644 --- a/clippy_lints/src/lib.register_suspicious.rs +++ b/clippy_lints/src/lib.register_suspicious.rs @@ -24,12 +24,12 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(loops::EMPTY_LOOP), LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES), LintId::of(loops::MUT_RANGE_BOUND), + LintId::of(matches::SIGNIFICANT_DROP_IN_SCRUTINEE), LintId::of(methods::NO_EFFECT_REPLACE), LintId::of(methods::SUSPICIOUS_MAP), LintId::of(mut_key::MUTABLE_KEY_TYPE), LintId::of(octal_escapes::OCTAL_ESCAPES), LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT), - LintId::of(significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE), LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7335b4a35c8c6..742409d758be9 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -367,7 +367,6 @@ mod self_named_constructors; mod semicolon_if_nothing_returned; mod serde_api; mod shadow; -mod significant_drop_in_scrutinee; mod single_char_lifetime_names; mod single_component_path_imports; mod size_of_in_element_count; @@ -886,7 +885,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation)); store.register_early_pass(|| Box::new(doc_link_with_quotes::DocLinkWithQuotes)); store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion)); - store.register_late_pass(|| Box::new(significant_drop_in_scrutinee::SignificantDropInScrutinee)); let allow_dbg_in_tests = conf.allow_dbg_in_tests; store.register_late_pass(move || Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests))); let cargo_ignore_publish = conf.cargo_ignore_publish; diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index a58c71ca5f808..3c0dc8041d817 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -25,6 +25,7 @@ mod needless_match; mod overlapping_arms; mod redundant_pattern_match; mod rest_pat_in_fully_bound_struct; +mod significant_drop_in_scrutinee; mod single_match; mod wild_in_or_pats; @@ -748,6 +749,82 @@ declare_clippy_lint! { "creation of a case altering match expression with non-compliant arms" } +declare_clippy_lint! { + /// ### What it does + /// Check for temporaries returned from function calls in a match scrutinee that have the + /// `clippy::has_significant_drop` attribute. + /// + /// ### Why is this bad? + /// The `clippy::has_significant_drop` attribute can be added to types whose Drop impls have + /// an important side-effect, such as unlocking a mutex, making it important for users to be + /// able to accurately understand their lifetimes. When a temporary is returned in a function + /// call in a match scrutinee, its lifetime lasts until the end of the match block, which may + /// be surprising. + /// + /// For `Mutex`es this can lead to a deadlock. This happens when the match scrutinee uses a + /// function call that returns a `MutexGuard` and then tries to lock again in one of the match + /// arms. In that case the `MutexGuard` in the scrutinee will not be dropped until the end of + /// the match block and thus will not unlock. + /// + /// ### Example + /// ```rust.ignore + /// # use std::sync::Mutex; + /// + /// # struct State {} + /// + /// # impl State { + /// # fn foo(&self) -> bool { + /// # true + /// # } + /// + /// # fn bar(&self) {} + /// # } + /// + /// + /// let mutex = Mutex::new(State {}); + /// + /// match mutex.lock().unwrap().foo() { + /// true => { + /// mutex.lock().unwrap().bar(); // Deadlock! + /// } + /// false => {} + /// }; + /// + /// println!("All done!"); + /// + /// ``` + /// Use instead: + /// ```rust + /// # use std::sync::Mutex; + /// + /// # struct State {} + /// + /// # impl State { + /// # fn foo(&self) -> bool { + /// # true + /// # } + /// + /// # fn bar(&self) {} + /// # } + /// + /// let mutex = Mutex::new(State {}); + /// + /// let is_foo = mutex.lock().unwrap().foo(); + /// match is_foo { + /// true => { + /// mutex.lock().unwrap().bar(); + /// } + /// false => {} + /// }; + /// + /// println!("All done!"); + /// ``` + #[clippy::version = "1.60.0"] + pub SIGNIFICANT_DROP_IN_SCRUTINEE, + suspicious, + "warns when a temporary of a type with a drop with a significant side-effect might have a surprising lifetime" +} + #[derive(Default)] pub struct Matches { msrv: Option, @@ -786,6 +863,7 @@ impl_lint_pass!(Matches => [ MANUAL_UNWRAP_OR, MATCH_ON_VEC_ITEMS, MATCH_STR_CASE_MISMATCH, + SIGNIFICANT_DROP_IN_SCRUTINEE, ]); impl<'tcx> LateLintPass<'tcx> for Matches { @@ -796,9 +874,12 @@ impl<'tcx> LateLintPass<'tcx> for Matches { let from_expansion = expr.span.from_expansion(); if let ExprKind::Match(ex, arms, source) = expr.kind { - if !span_starts_with(cx, expr.span, "match") { + if source == MatchSource::Normal && !span_starts_with(cx, expr.span, "match") { return; } + if matches!(source, MatchSource::Normal | MatchSource::ForLoopDesugar) { + significant_drop_in_scrutinee::check(cx, expr, ex, source); + } collapsible_match::check_match(cx, arms); if !from_expansion { diff --git a/clippy_lints/src/significant_drop_in_scrutinee.rs b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs similarity index 75% rename from clippy_lints/src/significant_drop_in_scrutinee.rs rename to clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 2f7819cb47048..a211dc18f9e1e 100644 --- a/clippy_lints/src/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -5,98 +5,24 @@ use clippy_utils::source::{indent_of, snippet}; use rustc_errors::{Applicability, Diagnostic}; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{Expr, ExprKind, MatchSource}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{Ty, TypeAndMut}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; -declare_clippy_lint! { - /// ### What it does - /// Check for temporaries returned from function calls in a match scrutinee that have the - /// `clippy::has_significant_drop` attribute. - /// - /// ### Why is this bad? - /// The `clippy::has_significant_drop` attribute can be added to types whose Drop impls have - /// an important side-effect, such as unlocking a mutex, making it important for users to be - /// able to accurately understand their lifetimes. When a temporary is returned in a function - /// call in a match scrutinee, its lifetime lasts until the end of the match block, which may - /// be surprising. - /// - /// For `Mutex`es this can lead to a deadlock. This happens when the match scrutinee uses a - /// function call that returns a `MutexGuard` and then tries to lock again in one of the match - /// arms. In that case the `MutexGuard` in the scrutinee will not be dropped until the end of - /// the match block and thus will not unlock. - /// - /// ### Example - /// ```rust.ignore - /// # use std::sync::Mutex; - /// - /// # struct State {} - /// - /// # impl State { - /// # fn foo(&self) -> bool { - /// # true - /// # } - /// - /// # fn bar(&self) {} - /// # } - /// - /// - /// let mutex = Mutex::new(State {}); - /// - /// match mutex.lock().unwrap().foo() { - /// true => { - /// mutex.lock().unwrap().bar(); // Deadlock! - /// } - /// false => {} - /// }; - /// - /// println!("All done!"); - /// - /// ``` - /// Use instead: - /// ```rust - /// # use std::sync::Mutex; - /// - /// # struct State {} - /// - /// # impl State { - /// # fn foo(&self) -> bool { - /// # true - /// # } - /// - /// # fn bar(&self) {} - /// # } - /// - /// let mutex = Mutex::new(State {}); - /// - /// let is_foo = mutex.lock().unwrap().foo(); - /// match is_foo { - /// true => { - /// mutex.lock().unwrap().bar(); - /// } - /// false => {} - /// }; - /// - /// println!("All done!"); - /// ``` - #[clippy::version = "1.60.0"] - pub SIGNIFICANT_DROP_IN_SCRUTINEE, - suspicious, - "warns when a temporary of a type with a drop with a significant side-effect might have a surprising lifetime" -} - -declare_lint_pass!(SignificantDropInScrutinee => [SIGNIFICANT_DROP_IN_SCRUTINEE]); +use super::SIGNIFICANT_DROP_IN_SCRUTINEE; -impl<'tcx> LateLintPass<'tcx> for SignificantDropInScrutinee { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if let Some((suggestions, message)) = has_significant_drop_in_scrutinee(cx, expr) { - for found in suggestions { - span_lint_and_then(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, found.found_span, message, |diag| { - set_diagnostic(diag, cx, expr, found); - }); - } +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + scrutinee: &'tcx Expr<'_>, + source: MatchSource, +) { + if let Some((suggestions, message)) = has_significant_drop_in_scrutinee(cx, scrutinee, source) { + for found in suggestions { + span_lint_and_then(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, found.found_span, message, |diag| { + set_diagnostic(diag, cx, expr, found); + }); } } } @@ -148,28 +74,18 @@ fn set_diagnostic<'tcx>(diag: &mut Diagnostic, cx: &LateContext<'tcx>, expr: &'t /// may have a surprising lifetime. fn has_significant_drop_in_scrutinee<'tcx, 'a>( cx: &'a LateContext<'tcx>, - expr: &'tcx Expr<'tcx>, + scrutinee: &'tcx Expr<'tcx>, + source: MatchSource, ) -> Option<(Vec, &'static str)> { - match expr.kind { - ExprKind::Match(match_expr, _, source) => { - match source { - MatchSource::Normal | MatchSource::ForLoopDesugar => { - let mut helper = SigDropHelper::new(cx); - helper.find_sig_drop(match_expr).map(|drops| { - let message = if source == MatchSource::Normal { - "temporary with significant drop in match scrutinee" - } else { - "temporary with significant drop in for loop" - }; - (drops, message) - }) - }, - // MatchSource of TryDesugar or AwaitDesugar is out of scope for this lint - MatchSource::TryDesugar | MatchSource::AwaitDesugar => None, - } - }, - _ => None, - } + let mut helper = SigDropHelper::new(cx); + helper.find_sig_drop(scrutinee).map(|drops| { + let message = if source == MatchSource::Normal { + "temporary with significant drop in match scrutinee" + } else { + "temporary with significant drop in for loop" + }; + (drops, message) + }) } struct SigDropHelper<'a, 'tcx> { From 67cb5ec29f0e1adb55f945a8acea20c47f586c56 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 3 Jun 2022 14:31:36 -0400 Subject: [PATCH 74/79] Move `TryErr` into `Matches` lint pass --- clippy_lints/src/lib.register_lints.rs | 2 +- clippy_lints/src/lib.register_restriction.rs | 2 +- clippy_lints/src/lib.rs | 2 - clippy_lints/src/matches/mod.rs | 41 ++++ clippy_lints/src/matches/try_err.rs | 145 +++++++++++++++ clippy_lints/src/try_err.rs | 186 ------------------- 6 files changed, 188 insertions(+), 190 deletions(-) create mode 100644 clippy_lints/src/matches/try_err.rs delete mode 100644 clippy_lints/src/try_err.rs diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 7fc6ff8a2fbe4..c8cebca89d587 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -279,6 +279,7 @@ store.register_lints(&[ matches::SIGNIFICANT_DROP_IN_SCRUTINEE, matches::SINGLE_MATCH, matches::SINGLE_MATCH_ELSE, + matches::TRY_ERR, matches::WILDCARD_ENUM_MATCH_ARM, matches::WILDCARD_IN_OR_PATTERNS, mem_forget::MEM_FORGET, @@ -521,7 +522,6 @@ store.register_lints(&[ transmute::USELESS_TRANSMUTE, transmute::WRONG_TRANSMUTE, transmuting_null::TRANSMUTING_NULL, - try_err::TRY_ERR, types::BORROWED_BOX, types::BOX_COLLECTION, types::LINKEDLIST, diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs index 304b595541aa6..942a53459fd92 100644 --- a/clippy_lints/src/lib.register_restriction.rs +++ b/clippy_lints/src/lib.register_restriction.rs @@ -30,6 +30,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION), LintId::of(map_err_ignore::MAP_ERR_IGNORE), LintId::of(matches::REST_PAT_IN_FULLY_BOUND_STRUCTS), + LintId::of(matches::TRY_ERR), LintId::of(matches::WILDCARD_ENUM_MATCH_ARM), LintId::of(mem_forget::MEM_FORGET), LintId::of(methods::CLONE_ON_REF_PTR), @@ -67,7 +68,6 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(strings::STRING_SLICE), LintId::of(strings::STRING_TO_STRING), LintId::of(strings::STR_TO_STRING), - LintId::of(try_err::TRY_ERR), LintId::of(types::RC_BUFFER), LintId::of(types::RC_MUTEX), LintId::of(undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 742409d758be9..d9941d75f787a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -385,7 +385,6 @@ mod trailing_empty_array; mod trait_bounds; mod transmute; mod transmuting_null; -mod try_err; mod types; mod undocumented_unsafe_blocks; mod unicode; @@ -700,7 +699,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ); 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)); diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index 3c0dc8041d817..fee7471f17d05 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -27,6 +27,7 @@ mod redundant_pattern_match; mod rest_pat_in_fully_bound_struct; mod significant_drop_in_scrutinee; mod single_match; +mod try_err; mod wild_in_or_pats; declare_clippy_lint! { @@ -825,6 +826,41 @@ declare_clippy_lint! { "warns when a temporary of a type with a drop with a significant side-effect might have a surprising lifetime" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usages of `Err(x)?`. + /// + /// ### Why is this bad? + /// The `?` operator is designed to allow calls that + /// can fail to be easily chained. For example, `foo()?.bar()` or + /// `foo(bar()?)`. Because `Err(x)?` can't be used that way (it will + /// always return), it is more clear to write `return Err(x)`. + /// + /// ### Example + /// ```rust + /// fn foo(fail: bool) -> Result { + /// if fail { + /// Err("failed")?; + /// } + /// Ok(0) + /// } + /// ``` + /// Could be written: + /// + /// ```rust + /// fn foo(fail: bool) -> Result { + /// if fail { + /// return Err("failed".into()); + /// } + /// Ok(0) + /// } + /// ``` + #[clippy::version = "1.38.0"] + pub TRY_ERR, + restriction, + "return errors explicitly rather than hiding them behind a `?`" +} + #[derive(Default)] pub struct Matches { msrv: Option, @@ -864,6 +900,7 @@ impl_lint_pass!(Matches => [ MATCH_ON_VEC_ITEMS, MATCH_STR_CASE_MISMATCH, SIGNIFICANT_DROP_IN_SCRUTINEE, + TRY_ERR, ]); impl<'tcx> LateLintPass<'tcx> for Matches { @@ -888,6 +925,10 @@ impl<'tcx> LateLintPass<'tcx> for Matches { wild_in_or_pats::check(cx, arms); } + if source == MatchSource::TryDesugar { + try_err::check(cx, expr, ex); + } + if !from_expansion && !contains_cfg_arm(cx, expr, ex, arms) { if source == MatchSource::Normal { if !(meets_msrv(self.msrv, msrvs::MATCHES_MACRO) diff --git a/clippy_lints/src/matches/try_err.rs b/clippy_lints/src/matches/try_err.rs new file mode 100644 index 0000000000000..0491a0679f37a --- /dev/null +++ b/clippy_lints/src/matches/try_err.rs @@ -0,0 +1,145 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{get_parent_expr, is_lang_ctor, match_def_path, paths}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::LangItem::ResultErr; +use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; +use rustc_span::{hygiene, sym}; + +use super::TRY_ERR; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutinee: &'tcx Expr<'_>) { + // Looks for a structure like this: + // match ::std::ops::Try::into_result(Err(5)) { + // ::std::result::Result::Err(err) => + // #[allow(unreachable_code)] + // return ::std::ops::Try::from_error(::std::convert::From::from(err)), + // ::std::result::Result::Ok(val) => + // #[allow(unreachable_code)] + // val, + // }; + if_chain! { + if let ExprKind::Call(match_fun, try_args) = scrutinee.kind; + if let ExprKind::Path(ref match_fun_path) = match_fun.kind; + if matches!(match_fun_path, QPath::LangItem(LangItem::TryTraitBranch, ..)); + if let Some(try_arg) = try_args.get(0); + if let ExprKind::Call(err_fun, err_args) = try_arg.kind; + if let Some(err_arg) = err_args.get(0); + if let ExprKind::Path(ref err_fun_path) = err_fun.kind; + if is_lang_ctor(cx, err_fun_path, ResultErr); + if let Some(return_ty) = find_return_type(cx, &expr.kind); + then { + let prefix; + let suffix; + let err_ty; + + if let Some(ty) = result_error_type(cx, return_ty) { + prefix = "Err("; + suffix = ")"; + err_ty = ty; + } else if let Some(ty) = poll_result_error_type(cx, return_ty) { + prefix = "Poll::Ready(Err("; + suffix = "))"; + err_ty = ty; + } else if let Some(ty) = poll_option_result_error_type(cx, return_ty) { + prefix = "Poll::Ready(Some(Err("; + suffix = ")))"; + err_ty = ty; + } else { + return; + }; + + let expr_err_ty = cx.typeck_results().expr_ty(err_arg); + let span = hygiene::walk_chain(err_arg.span, try_arg.span.ctxt()); + let mut applicability = Applicability::MachineApplicable; + let origin_snippet = snippet_with_applicability(cx, span, "_", &mut applicability); + let ret_prefix = if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::Ret(_))) { + "" // already returns + } else { + "return " + }; + let suggestion = if err_ty == expr_err_ty { + format!("{}{}{}{}", ret_prefix, prefix, origin_snippet, suffix) + } else { + format!("{}{}{}.into(){}", ret_prefix, prefix, origin_snippet, suffix) + }; + + span_lint_and_sugg( + cx, + TRY_ERR, + expr.span, + "returning an `Err(_)` with the `?` operator", + "try this", + suggestion, + applicability, + ); + } + } +} + +/// Finds function return type by examining return expressions in match arms. +fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> Option> { + if let ExprKind::Match(_, arms, MatchSource::TryDesugar) = expr { + for arm in arms.iter() { + if let ExprKind::Ret(Some(ret)) = arm.body.kind { + return Some(cx.typeck_results().expr_ty(ret)); + } + } + } + None +} + +/// Extracts the error type from Result. +fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { + if_chain! { + if let ty::Adt(_, subst) = ty.kind(); + if is_type_diagnostic_item(cx, ty, sym::Result); + then { + Some(subst.type_at(1)) + } else { + None + } + } +} + +/// Extracts the error type from Poll>. +fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { + if_chain! { + if let ty::Adt(def, subst) = ty.kind(); + if match_def_path(cx, def.did(), &paths::POLL); + let ready_ty = subst.type_at(0); + + if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); + if cx.tcx.is_diagnostic_item(sym::Result, ready_def.did()); + then { + Some(ready_subst.type_at(1)) + } else { + None + } + } +} + +/// Extracts the error type from Poll>>. +fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { + if_chain! { + if let ty::Adt(def, subst) = ty.kind(); + if match_def_path(cx, def.did(), &paths::POLL); + let ready_ty = subst.type_at(0); + + if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); + if cx.tcx.is_diagnostic_item(sym::Option, ready_def.did()); + let some_ty = ready_subst.type_at(0); + + if let ty::Adt(some_def, some_subst) = some_ty.kind(); + if cx.tcx.is_diagnostic_item(sym::Result, some_def.did()); + then { + Some(some_subst.type_at(1)) + } else { + None + } + } +} diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs deleted file mode 100644 index e108f7be12e6a..0000000000000 --- a/clippy_lints/src/try_err.rs +++ /dev/null @@ -1,186 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{get_parent_expr, is_lang_ctor, match_def_path, paths}; -use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir::LangItem::ResultErr; -use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Ty}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{hygiene, sym}; - -declare_clippy_lint! { - /// ### What it does - /// Checks for usages of `Err(x)?`. - /// - /// ### Why is this bad? - /// The `?` operator is designed to allow calls that - /// can fail to be easily chained. For example, `foo()?.bar()` or - /// `foo(bar()?)`. Because `Err(x)?` can't be used that way (it will - /// always return), it is more clear to write `return Err(x)`. - /// - /// ### Example - /// ```rust - /// fn foo(fail: bool) -> Result { - /// if fail { - /// Err("failed")?; - /// } - /// Ok(0) - /// } - /// ``` - /// Could be written: - /// - /// ```rust - /// fn foo(fail: bool) -> Result { - /// if fail { - /// return Err("failed".into()); - /// } - /// Ok(0) - /// } - /// ``` - #[clippy::version = "1.38.0"] - pub TRY_ERR, - restriction, - "return errors explicitly rather than hiding them behind a `?`" -} - -declare_lint_pass!(TryErr => [TRY_ERR]); - -impl<'tcx> LateLintPass<'tcx> for TryErr { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - // Looks for a structure like this: - // match ::std::ops::Try::into_result(Err(5)) { - // ::std::result::Result::Err(err) => - // #[allow(unreachable_code)] - // return ::std::ops::Try::from_error(::std::convert::From::from(err)), - // ::std::result::Result::Ok(val) => - // #[allow(unreachable_code)] - // val, - // }; - if_chain! { - if !in_external_macro(cx.tcx.sess, expr.span); - if let ExprKind::Match(match_arg, _, MatchSource::TryDesugar) = expr.kind; - if let ExprKind::Call(match_fun, try_args) = match_arg.kind; - if let ExprKind::Path(ref match_fun_path) = match_fun.kind; - if matches!(match_fun_path, QPath::LangItem(LangItem::TryTraitBranch, ..)); - if let Some(try_arg) = try_args.get(0); - if let ExprKind::Call(err_fun, err_args) = try_arg.kind; - if let Some(err_arg) = err_args.get(0); - if let ExprKind::Path(ref err_fun_path) = err_fun.kind; - if is_lang_ctor(cx, err_fun_path, ResultErr); - if let Some(return_ty) = find_return_type(cx, &expr.kind); - then { - let prefix; - let suffix; - let err_ty; - - if let Some(ty) = result_error_type(cx, return_ty) { - prefix = "Err("; - suffix = ")"; - err_ty = ty; - } else if let Some(ty) = poll_result_error_type(cx, return_ty) { - prefix = "Poll::Ready(Err("; - suffix = "))"; - err_ty = ty; - } else if let Some(ty) = poll_option_result_error_type(cx, return_ty) { - prefix = "Poll::Ready(Some(Err("; - suffix = ")))"; - err_ty = ty; - } else { - return; - }; - - let expr_err_ty = cx.typeck_results().expr_ty(err_arg); - let span = hygiene::walk_chain(err_arg.span, try_arg.span.ctxt()); - let mut applicability = Applicability::MachineApplicable; - let origin_snippet = snippet_with_applicability(cx, span, "_", &mut applicability); - let ret_prefix = if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::Ret(_))) { - "" // already returns - } else { - "return " - }; - let suggestion = if err_ty == expr_err_ty { - format!("{}{}{}{}", ret_prefix, prefix, origin_snippet, suffix) - } else { - format!("{}{}{}.into(){}", ret_prefix, prefix, origin_snippet, suffix) - }; - - span_lint_and_sugg( - cx, - TRY_ERR, - expr.span, - "returning an `Err(_)` with the `?` operator", - "try this", - suggestion, - applicability, - ); - } - } - } -} - -/// Finds function return type by examining return expressions in match arms. -fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> Option> { - if let ExprKind::Match(_, arms, MatchSource::TryDesugar) = expr { - for arm in arms.iter() { - if let ExprKind::Ret(Some(ret)) = arm.body.kind { - return Some(cx.typeck_results().expr_ty(ret)); - } - } - } - None -} - -/// Extracts the error type from Result. -fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { - if_chain! { - if let ty::Adt(_, subst) = ty.kind(); - if is_type_diagnostic_item(cx, ty, sym::Result); - then { - Some(subst.type_at(1)) - } else { - None - } - } -} - -/// Extracts the error type from Poll>. -fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { - if_chain! { - if let ty::Adt(def, subst) = ty.kind(); - if match_def_path(cx, def.did(), &paths::POLL); - let ready_ty = subst.type_at(0); - - if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); - if cx.tcx.is_diagnostic_item(sym::Result, ready_def.did()); - then { - Some(ready_subst.type_at(1)) - } else { - None - } - } -} - -/// Extracts the error type from Poll>>. -fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { - if_chain! { - if let ty::Adt(def, subst) = ty.kind(); - if match_def_path(cx, def.did(), &paths::POLL); - let ready_ty = subst.type_at(0); - - if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); - if cx.tcx.is_diagnostic_item(sym::Option, ready_def.did()); - let some_ty = ready_subst.type_at(0); - - if let ty::Adt(some_def, some_subst) = some_ty.kind(); - if cx.tcx.is_diagnostic_item(sym::Result, some_def.did()); - then { - Some(some_subst.type_at(1)) - } else { - None - } - } -} From 68c411ff943c71cd86d18a0b50f23b8da9d78181 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 3 Jun 2022 19:01:44 -0400 Subject: [PATCH 75/79] Move `ManualMap` into `Matches` lint pass --- clippy_lints/src/lib.register_all.rs | 2 +- clippy_lints/src/lib.register_lints.rs | 2 +- clippy_lints/src/lib.register_style.rs | 2 +- clippy_lints/src/lib.rs | 2 - clippy_lints/src/manual_map.rs | 316 ------------------------- clippy_lints/src/matches/manual_map.rs | 306 ++++++++++++++++++++++++ clippy_lints/src/matches/mod.rs | 30 +++ 7 files changed, 339 insertions(+), 321 deletions(-) delete mode 100644 clippy_lints/src/manual_map.rs create mode 100644 clippy_lints/src/matches/manual_map.rs diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 435c0829a6770..d4ec046d0bb08 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -133,7 +133,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(main_recursion::MAIN_RECURSION), LintId::of(manual_async_fn::MANUAL_ASYNC_FN), LintId::of(manual_bits::MANUAL_BITS), - LintId::of(manual_map::MANUAL_MAP), LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(manual_strip::MANUAL_STRIP), LintId::of(map_clone::MAP_CLONE), @@ -142,6 +141,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(match_result_ok::MATCH_RESULT_OK), LintId::of(matches::COLLAPSIBLE_MATCH), LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), + LintId::of(matches::MANUAL_MAP), LintId::of(matches::MANUAL_UNWRAP_OR), LintId::of(matches::MATCH_AS_REF), LintId::of(matches::MATCH_LIKE_MATCHES_MACRO), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index c8cebca89d587..4cf38e8ab15bc 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -250,7 +250,6 @@ store.register_lints(&[ manual_assert::MANUAL_ASSERT, manual_async_fn::MANUAL_ASYNC_FN, manual_bits::MANUAL_BITS, - manual_map::MANUAL_MAP, manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, manual_ok_or::MANUAL_OK_OR, manual_strip::MANUAL_STRIP, @@ -261,6 +260,7 @@ store.register_lints(&[ match_result_ok::MATCH_RESULT_OK, matches::COLLAPSIBLE_MATCH, matches::INFALLIBLE_DESTRUCTURING_MATCH, + matches::MANUAL_MAP, matches::MANUAL_UNWRAP_OR, matches::MATCH_AS_REF, matches::MATCH_BOOL, diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index 07e90b8b656e5..35575351784a2 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -45,12 +45,12 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(main_recursion::MAIN_RECURSION), LintId::of(manual_async_fn::MANUAL_ASYNC_FN), LintId::of(manual_bits::MANUAL_BITS), - LintId::of(manual_map::MANUAL_MAP), LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(map_clone::MAP_CLONE), LintId::of(match_result_ok::MATCH_RESULT_OK), LintId::of(matches::COLLAPSIBLE_MATCH), LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), + LintId::of(matches::MANUAL_MAP), LintId::of(matches::MATCH_LIKE_MATCHES_MACRO), LintId::of(matches::MATCH_OVERLAPPING_ARM), LintId::of(matches::MATCH_REF_PATS), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d9941d75f787a..c924bfe2ad611 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -279,7 +279,6 @@ mod main_recursion; mod manual_assert; mod manual_async_fn; mod manual_bits; -mod manual_map; mod manual_non_exhaustive; mod manual_ok_or; mod manual_strip; @@ -845,7 +844,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: }); store.register_late_pass(|| Box::new(redundant_slicing::RedundantSlicing)); store.register_late_pass(|| Box::new(from_str_radix_10::FromStrRadix10)); - store.register_late_pass(|| Box::new(manual_map::ManualMap)); store.register_late_pass(move || Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv))); store.register_late_pass(|| Box::new(bool_assert_comparison::BoolAssertComparison)); store.register_early_pass(move || Box::new(module_style::ModStyle)); diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs deleted file mode 100644 index 230ae029ed9d2..0000000000000 --- a/clippy_lints/src/manual_map.rs +++ /dev/null @@ -1,316 +0,0 @@ -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::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, type_is_unsafe_function}; -use clippy_utils::{ - can_move_expr_to_closure, in_constant, is_else_clause, is_lang_ctor, is_lint_allowed, path_to_local_id, - peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, CaptureKind, -}; -use rustc_ast::util::parser::PREC_POSTFIX; -use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionNone, OptionSome}; -use rustc_hir::{ - def::Res, Arm, BindingAnnotation, Block, BlockCheckMode, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, - QPath, UnsafeSource, -}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{sym, SyntaxContext}; - -declare_clippy_lint! { - /// ### What it does - /// Checks for usages of `match` which could be implemented using `map` - /// - /// ### Why is this bad? - /// Using the `map` method is clearer and more concise. - /// - /// ### Example - /// ```rust - /// match Some(0) { - /// Some(x) => Some(x + 1), - /// None => None, - /// }; - /// ``` - /// Use instead: - /// ```rust - /// Some(0).map(|x| x + 1); - /// ``` - #[clippy::version = "1.52.0"] - pub MANUAL_MAP, - style, - "reimplementation of `map`" -} - -declare_lint_pass!(ManualMap => [MANUAL_MAP]); - -impl<'tcx> LateLintPass<'tcx> for ManualMap { - #[expect(clippy::too_many_lines)] - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx 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; - } - - 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) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option)) - { - 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; - } - - let some_expr = match get_some_expr(cx, some_expr, false, expr_ctxt) { - Some(expr) => expr, - None => return, - }; - - // These two lints will go back and forth with each other. - if cx.typeck_results().expr_ty(some_expr.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.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)); - - 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.expr) { - Some(captures) => { - // Check if captures the closure will need conflict with borrows made in the scrutinee. - // TODO: check all the references made in the scrutinee expression. This will require interacting - // with the borrow checker. Currently only `[.]*` is checked for. - if let Some(binding_ref_mutability) = binding_ref { - let e = peel_hir_expr_while(scrutinee, |e| match e.kind { - ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e), - _ => None, - }); - if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind { - match captures.get(l) { - Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return, - Some(CaptureKind::Ref(Mutability::Not)) if binding_ref_mutability == Mutability::Mut => { - return; - }, - Some(CaptureKind::Ref(Mutability::Not)) | None => (), - } - } - } - }, - None => return, - }; - - let mut app = Applicability::MachineApplicable; - - // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or - // 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() - }; - - let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind { - if_chain! { - if !some_expr.needs_unsafe_block; - if let Some(func) = can_pass_as_func(cx, id, some_expr.expr); - if func.span.ctxt() == some_expr.expr.span.ctxt(); - then { - snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() - } else { - if path_to_local_id(some_expr.expr, id) - && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id) - && binding_ref.is_some() - { - return; - } - - // `ref` and `ref mut` annotations were handled earlier. - let annotation = if matches!(annotation, BindingAnnotation::Mutable) { - "mut " - } else { - "" - }; - let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0; - if some_expr.needs_unsafe_block { - format!("|{}{}| unsafe {{ {} }}", annotation, some_binding, expr_snip) - } else { - format!("|{}{}| {}", annotation, some_binding, expr_snip) - } - } - } - } else if !is_wild_none && explicit_ref.is_none() { - // TODO: handle explicit reference annotations. - let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0; - let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0; - if some_expr.needs_unsafe_block { - format!("|{}| unsafe {{ {} }}", pat_snip, expr_snip) - } else { - format!("|{}| {}", pat_snip, expr_snip) - } - } 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 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. -// Returns the function to be passed to `map` if it exists. -fn can_pass_as_func<'tcx>(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() - && !type_is_unsafe_function(cx, cx.typeck_results().expr_ty(func).peel_refs()) => - { - Some(func) - }, - _ => None, - } -} - -enum OptionPat<'a> { - Wild, - None, - Some { - // The pattern contained in the `Some` tuple. - pattern: &'a Pat<'a>, - // The number of references before the `Some` tuple. - // e.g. `&&Some(_)` has a ref count of 2. - ref_count: usize, - }, -} - -struct SomeExpr<'tcx> { - expr: &'tcx Expr<'tcx>, - needs_unsafe_block: bool, -} - -// Try to parse into a recognized `Option` pattern. -// i.e. `_`, `None`, `Some(..)`, or a reference to any of those. -fn try_parse_pattern<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option> { - fn f<'tcx>( - cx: &LateContext<'tcx>, - pat: &'tcx Pat<'_>, - 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::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone) => Some(OptionPat::None), - PatKind::TupleStruct(ref qpath, [pattern], _) - if is_lang_ctor(cx, qpath, OptionSome) && pat.span.ctxt() == ctxt => - { - Some(OptionPat::Some { pattern, ref_count }) - }, - _ => None, - } - } - f(cx, pat, 0, ctxt) -} - -// Checks for an expression wrapped by the `Some` constructor. Returns the contained expression. -fn get_some_expr<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, - needs_unsafe_block: bool, - ctxt: SyntaxContext, -) -> Option> { - // TODO: Allow more complex expressions. - match expr.kind { - ExprKind::Call( - Expr { - kind: ExprKind::Path(ref qpath), - .. - }, - [arg], - ) if ctxt == expr.span.ctxt() && is_lang_ctor(cx, qpath, OptionSome) => Some(SomeExpr { - expr: arg, - needs_unsafe_block, - }), - ExprKind::Block( - Block { - stmts: [], - expr: Some(expr), - rules, - .. - }, - _, - ) => get_some_expr( - cx, - expr, - needs_unsafe_block || *rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), - ctxt, - ), - _ => None, - } -} - -// Checks for the `None` value. -fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - matches!(peel_blocks(expr).kind, ExprKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone)) -} diff --git a/clippy_lints/src/matches/manual_map.rs b/clippy_lints/src/matches/manual_map.rs new file mode 100644 index 0000000000000..542905a2d763c --- /dev/null +++ b/clippy_lints/src/matches/manual_map.rs @@ -0,0 +1,306 @@ +use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; +use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function}; +use clippy_utils::{ + can_move_expr_to_closure, is_else_clause, is_lang_ctor, is_lint_allowed, path_to_local_id, peel_blocks, + peel_hir_expr_refs, peel_hir_expr_while, CaptureKind, +}; +use rustc_ast::util::parser::PREC_POSTFIX; +use rustc_errors::Applicability; +use rustc_hir::LangItem::{OptionNone, OptionSome}; +use rustc_hir::{ + def::Res, Arm, BindingAnnotation, Block, BlockCheckMode, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, + QPath, UnsafeSource, +}; +use rustc_lint::LateContext; +use rustc_span::{sym, SyntaxContext}; + +use super::MANUAL_MAP; + +pub(super) fn check_match<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + scrutinee: &'tcx Expr<'_>, + arms: &'tcx [Arm<'_>], +) { + if let [arm1, arm2] = arms + && arm1.guard.is_none() + && arm2.guard.is_none() + { + check(cx, expr, scrutinee, arm1.pat, arm1.body, Some(arm2.pat), arm2.body); + } +} + +pub(super) fn check_if_let<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + let_pat: &'tcx Pat<'_>, + let_expr: &'tcx Expr<'_>, + then_expr: &'tcx Expr<'_>, + else_expr: &'tcx Expr<'_>, +) { + check(cx, expr, let_expr, let_pat, then_expr, None, else_expr); +} + +#[expect(clippy::too_many_lines)] +fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + scrutinee: &'tcx Expr<'_>, + then_pat: &'tcx Pat<'_>, + then_body: &'tcx Expr<'_>, + else_pat: Option<&'tcx Pat<'_>>, + else_body: &'tcx 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) + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option)) + { + 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; + } + + let some_expr = match get_some_expr(cx, some_expr, false, expr_ctxt) { + Some(expr) => expr, + None => return, + }; + + // These two lints will go back and forth with each other. + if cx.typeck_results().expr_ty(some_expr.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.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)); + + 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.expr) { + Some(captures) => { + // Check if captures the closure will need conflict with borrows made in the scrutinee. + // TODO: check all the references made in the scrutinee expression. This will require interacting + // with the borrow checker. Currently only `[.]*` is checked for. + if let Some(binding_ref_mutability) = binding_ref { + let e = peel_hir_expr_while(scrutinee, |e| match e.kind { + ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e), + _ => None, + }); + if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind { + match captures.get(l) { + Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return, + Some(CaptureKind::Ref(Mutability::Not)) if binding_ref_mutability == Mutability::Mut => { + return; + }, + Some(CaptureKind::Ref(Mutability::Not)) | None => (), + } + } + } + }, + None => return, + }; + + let mut app = Applicability::MachineApplicable; + + // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or + // 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() + }; + + let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind { + if_chain! { + if !some_expr.needs_unsafe_block; + if let Some(func) = can_pass_as_func(cx, id, some_expr.expr); + if func.span.ctxt() == some_expr.expr.span.ctxt(); + then { + snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() + } else { + if path_to_local_id(some_expr.expr, id) + && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id) + && binding_ref.is_some() + { + return; + } + + // `ref` and `ref mut` annotations were handled earlier. + let annotation = if matches!(annotation, BindingAnnotation::Mutable) { + "mut " + } else { + "" + }; + let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0; + if some_expr.needs_unsafe_block { + format!("|{}{}| unsafe {{ {} }}", annotation, some_binding, expr_snip) + } else { + format!("|{}{}| {}", annotation, some_binding, expr_snip) + } + } + } + } else if !is_wild_none && explicit_ref.is_none() { + // TODO: handle explicit reference annotations. + let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0; + let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0; + if some_expr.needs_unsafe_block { + format!("|{}| unsafe {{ {} }}", pat_snip, expr_snip) + } else { + format!("|{}| {}", pat_snip, expr_snip) + } + } 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 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. +// Returns the function to be passed to `map` if it exists. +fn can_pass_as_func<'tcx>(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() + && !type_is_unsafe_function(cx, cx.typeck_results().expr_ty(func).peel_refs()) => + { + Some(func) + }, + _ => None, + } +} + +enum OptionPat<'a> { + Wild, + None, + Some { + // The pattern contained in the `Some` tuple. + pattern: &'a Pat<'a>, + // The number of references before the `Some` tuple. + // e.g. `&&Some(_)` has a ref count of 2. + ref_count: usize, + }, +} + +struct SomeExpr<'tcx> { + expr: &'tcx Expr<'tcx>, + needs_unsafe_block: bool, +} + +// Try to parse into a recognized `Option` pattern. +// i.e. `_`, `None`, `Some(..)`, or a reference to any of those. +fn try_parse_pattern<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option> { + fn f<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + 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::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone) => Some(OptionPat::None), + PatKind::TupleStruct(ref qpath, [pattern], _) + if is_lang_ctor(cx, qpath, OptionSome) && pat.span.ctxt() == ctxt => + { + Some(OptionPat::Some { pattern, ref_count }) + }, + _ => None, + } + } + f(cx, pat, 0, ctxt) +} + +// Checks for an expression wrapped by the `Some` constructor. Returns the contained expression. +fn get_some_expr<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + needs_unsafe_block: bool, + ctxt: SyntaxContext, +) -> Option> { + // TODO: Allow more complex expressions. + match expr.kind { + ExprKind::Call( + Expr { + kind: ExprKind::Path(ref qpath), + .. + }, + [arg], + ) if ctxt == expr.span.ctxt() && is_lang_ctor(cx, qpath, OptionSome) => Some(SomeExpr { + expr: arg, + needs_unsafe_block, + }), + ExprKind::Block( + Block { + stmts: [], + expr: Some(expr), + rules, + .. + }, + _, + ) => get_some_expr( + cx, + expr, + needs_unsafe_block || *rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), + ctxt, + ), + _ => None, + } +} + +// Checks for the `None` value. +fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + matches!(peel_blocks(expr).kind, ExprKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone)) +} diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index fee7471f17d05..d1e42f39e470d 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -10,6 +10,7 @@ use rustc_span::{Span, SpanData, SyntaxContext}; mod collapsible_match; mod infallible_destructuring_match; +mod manual_map; mod manual_unwrap_or; mod match_as_ref; mod match_bool; @@ -861,6 +862,30 @@ declare_clippy_lint! { "return errors explicitly rather than hiding them behind a `?`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usages of `match` which could be implemented using `map` + /// + /// ### Why is this bad? + /// Using the `map` method is clearer and more concise. + /// + /// ### Example + /// ```rust + /// match Some(0) { + /// Some(x) => Some(x + 1), + /// None => None, + /// }; + /// ``` + /// Use instead: + /// ```rust + /// Some(0).map(|x| x + 1); + /// ``` + #[clippy::version = "1.52.0"] + pub MANUAL_MAP, + style, + "reimplementation of `map`" +} + #[derive(Default)] pub struct Matches { msrv: Option, @@ -901,6 +926,7 @@ impl_lint_pass!(Matches => [ MATCH_STR_CASE_MISMATCH, SIGNIFICANT_DROP_IN_SCRUTINEE, TRY_ERR, + MANUAL_MAP, ]); impl<'tcx> LateLintPass<'tcx> for Matches { @@ -949,6 +975,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { if !in_constant(cx, expr.hir_id) { manual_unwrap_or::check(cx, expr, ex, arms); + manual_map::check_match(cx, expr, ex, arms); } if self.infallible_destructuring_match_linted { @@ -973,6 +1000,9 @@ impl<'tcx> LateLintPass<'tcx> for Matches { else_expr, ); } + if !in_constant(cx, expr.hir_id) { + manual_map::check_if_let(cx, expr, if_let.let_pat, if_let.let_expr, if_let.if_then, else_expr); + } } redundant_pattern_match::check_if_let( cx, From 36b18924a7aef4f901af177ef54971d610297bdd Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Sat, 4 Jun 2022 12:54:20 +0200 Subject: [PATCH 76/79] Bump nightly version -> 2022-06-04 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 997e7ba9382b2..2386a751f04f6 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-05-19" +channel = "nightly-2022-06-04" components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] From c22d4e6b3462cdb2b5c90ee57c487a252a0da776 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Sat, 4 Jun 2022 14:04:35 +0200 Subject: [PATCH 77/79] Remove unnecessary clap_derive dependency added in 9ee211af The fixed issue in this commit can be tested without depending on clap/clap_derive. This updates the test case to do so. --- src/tools/clippy/Cargo.toml | 1 - src/tools/clippy/tests/compile-test.rs | 3 --- .../clippy/tests/ui/auxiliary/proc_macro_attr.rs | 5 +++++ .../tests/ui/empty_line_after_outer_attribute.rs | 9 +++------ .../tests/ui/empty_line_after_outer_attribute.stderr | 12 ++++++------ 5 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index d23d681df00c1..3c8b758d53dca 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -40,7 +40,6 @@ filetime = "0.2" rustc-workspace-hack = "1.0" # UI test dependencies -clap = { version = "3.1", features = ["derive"] } clippy_utils = { path = "clippy_utils" } derive-new = "0.5" if_chain = "1.0" diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs index 7d21983572324..04c2eeff08b6f 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -23,7 +23,6 @@ const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal"); /// All crates used in UI tests are listed here static TEST_DEPENDENCIES: &[&str] = &[ - "clap", "clippy_utils", "derive_new", "futures", @@ -42,8 +41,6 @@ static TEST_DEPENDENCIES: &[&str] = &[ // Test dependencies may need an `extern crate` here to ensure that they show up // in the depinfo file (otherwise cargo thinks they are unused) #[allow(unused_extern_crates)] -extern crate clap; -#[allow(unused_extern_crates)] extern crate clippy_utils; #[allow(unused_extern_crates)] extern crate derive_new; diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs index e370a98df1acf..ae2cc2492f414 100644 --- a/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs +++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs @@ -19,6 +19,11 @@ use syn::{ parse_quote, FnArg, ImplItem, ItemImpl, ItemTrait, Lifetime, Pat, PatIdent, PatType, Signature, TraitItem, Type, }; +#[proc_macro_attribute] +pub fn dummy(_args: TokenStream, input: TokenStream) -> TokenStream { + input +} + #[proc_macro_attribute] pub fn fake_async_trait(_args: TokenStream, input: TokenStream) -> TokenStream { let mut item = parse_macro_input!(input as ItemTrait); diff --git a/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs b/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs index d15c84d7438a0..697412c00275e 100644 --- a/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs +++ b/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs @@ -4,9 +4,6 @@ #![feature(custom_inner_attributes)] #![rustfmt::skip] -#[macro_use] -extern crate clap; - #[macro_use] extern crate proc_macro_attr; @@ -113,10 +110,10 @@ pub trait Bazz { } } -#[derive(clap::Parser)] -#[clap(after_help = "This ia a help message. +#[derive(Clone, Copy)] +#[dummy(string = "first line -You're welcome. +second line ")] pub struct Args; diff --git a/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.stderr b/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.stderr index acc3edef9b928..594fca44a3210 100644 --- a/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.stderr +++ b/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.stderr @@ -1,5 +1,5 @@ error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:14:1 + --> $DIR/empty_line_after_outer_attribute.rs:11:1 | LL | / #[crate_type = "lib"] LL | | @@ -10,7 +10,7 @@ LL | | fn with_one_newline_and_comment() { assert!(true) } = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings` error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:26:1 + --> $DIR/empty_line_after_outer_attribute.rs:23:1 | LL | / #[crate_type = "lib"] LL | | @@ -18,7 +18,7 @@ LL | | fn with_one_newline() { assert!(true) } | |_ error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:31:1 + --> $DIR/empty_line_after_outer_attribute.rs:28:1 | LL | / #[crate_type = "lib"] LL | | @@ -27,7 +27,7 @@ LL | | fn with_two_newlines() { assert!(true) } | |_ error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:38:1 + --> $DIR/empty_line_after_outer_attribute.rs:35:1 | LL | / #[crate_type = "lib"] LL | | @@ -35,7 +35,7 @@ LL | | enum Baz { | |_ error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:46:1 + --> $DIR/empty_line_after_outer_attribute.rs:43:1 | LL | / #[crate_type = "lib"] LL | | @@ -43,7 +43,7 @@ LL | | struct Foo { | |_ error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:54:1 + --> $DIR/empty_line_after_outer_attribute.rs:51:1 | LL | / #[crate_type = "lib"] LL | | From 586cbd0229e09ff8edec5876d5acd1cefa474467 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Sat, 4 Jun 2022 14:07:44 +0200 Subject: [PATCH 78/79] Update Cargo.lock This updates the Cargo.lock file for Clippy: - parking_lot 0.11.2 -> 0.12 (now 2 versions in parking lot) - rustfix 0.6.0 -> 0.6.1 (now only one version left) - compiletest_rs 0.7.1 -> 0.8 - clap 2.34.0 -> 3.1.1 --- Cargo.lock | 119 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 87 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dd1869fb01f09..564cd3fd5834a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -353,7 +353,7 @@ dependencies = [ "percent-encoding 2.1.0", "pretty_env_logger", "rustc-workspace-hack", - "rustfix 0.6.0", + "rustfix", "semver", "serde", "serde_ignored", @@ -637,7 +637,7 @@ dependencies = [ "futures 0.3.19", "if_chain", "itertools", - "parking_lot", + "parking_lot 0.12.1", "quote", "regex", "rustc-semver", @@ -657,7 +657,7 @@ name = "clippy_dev" version = "0.0.1" dependencies = [ "aho-corasick", - "clap 2.34.0", + "clap 3.1.1", "indoc", "itertools", "opener", @@ -766,7 +766,7 @@ dependencies = [ "libc", "miow", "regex", - "rustfix 0.6.0", + "rustfix", "serde", "serde_json", "tracing", @@ -778,9 +778,9 @@ dependencies = [ [[package]] name = "compiletest_rs" -version = "0.7.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29843cb8d351febf86557681d049d1e1652b81a086a190fa1173c07fd17fbf83" +checksum = "262134ef87408da1ddfe45e33daa0ca43b75286d6b1076446e602d264cf9847e" dependencies = [ "diff", "filetime", @@ -790,7 +790,7 @@ dependencies = [ "log", "miow", "regex", - "rustfix 0.5.1", + "rustfix", "serde", "serde_derive", "serde_json", @@ -2040,7 +2040,7 @@ dependencies = [ "jsonrpc-server-utils", "log", "parity-tokio-ipc", - "parking_lot", + "parking_lot 0.11.2", "tower-service", ] @@ -2054,7 +2054,7 @@ dependencies = [ "jsonrpc-core", "lazy_static", "log", - "parking_lot", + "parking_lot 0.11.2", "rand 0.7.3", "serde", ] @@ -2202,10 +2202,11 @@ version = "0.1.0" [[package]] name = "lock_api" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" dependencies = [ + "autocfg", "scopeguard", ] @@ -2340,7 +2341,7 @@ checksum = "78f7a41bc6f856a2cf0e95094ad5121f82500e2d9a0f3c0171d98f6566d8117d" dependencies = [ "log", "memmap2", - "parking_lot", + "parking_lot 0.11.2", "perf-event-open-sys", "rustc-hash", "smallvec", @@ -2354,7 +2355,7 @@ checksum = "bd460fad6e55ca82fa0cd9dab0d315294188fd9ec6efbf4105e5635d4872ef9c" dependencies = [ "log", "memmap2", - "parking_lot", + "parking_lot 0.11.2", "perf-event-open-sys", "rustc-hash", "smallvec", @@ -2703,7 +2704,17 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ "instant", "lock_api", - "parking_lot_core", + "parking_lot_core 0.8.5", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.3", ] [[package]] @@ -2720,6 +2731,19 @@ dependencies = [ "winapi", ] +[[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + [[package]] name = "pathdiff" version = "0.2.1" @@ -3731,7 +3755,7 @@ dependencies = [ "libc", "measureme 10.0.0", "memmap2", - "parking_lot", + "parking_lot 0.11.2", "rustc-hash", "rustc-rayon", "rustc-rayon-core", @@ -4315,7 +4339,7 @@ dependencies = [ name = "rustc_query_system" version = "0.0.0" dependencies = [ - "parking_lot", + "parking_lot 0.11.2", "rustc-rayon-core", "rustc_arena", "rustc_ast", @@ -4618,21 +4642,9 @@ dependencies = [ [[package]] name = "rustfix" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2c50b74badcddeb8f7652fa8323ce440b95286f8e4b64ebfd871c609672704e" -dependencies = [ - "anyhow", - "log", - "serde", - "serde_json", -] - -[[package]] -name = "rustfix" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0be05fc0675ef4f47119dc39cfc46636bb77d4fc4ef1bd851b9c3f7697f32a" +checksum = "ecd2853d9e26988467753bd9912c3a126f642d05d229a4b53f5752ee36c56481" dependencies = [ "anyhow", "log", @@ -5022,7 +5034,7 @@ checksum = "33994d0838dc2d152d17a62adf608a869b5e846b65b389af7f3dbc1de45c5b26" dependencies = [ "lazy_static", "new_debug_unreachable", - "parking_lot", + "parking_lot 0.11.2", "phf_shared", "precomputed-hash", "serde", @@ -5451,7 +5463,7 @@ dependencies = [ "ansi_term", "lazy_static", "matchers", - "parking_lot", + "parking_lot 0.11.2", "regex", "sharded-slab", "smallvec", @@ -5853,6 +5865,49 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + [[package]] name = "xattr" version = "0.2.2" From 9525e0cb97fec55ecb6985e818365c6a2a278ba1 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 6 Jun 2022 11:14:59 +0200 Subject: [PATCH 79/79] Add winnt feature to winapi in rustc-workspace-hack --- src/tools/rustc-workspace-hack/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/rustc-workspace-hack/Cargo.toml b/src/tools/rustc-workspace-hack/Cargo.toml index 4bda3fb22c280..a79290e7a6bc3 100644 --- a/src/tools/rustc-workspace-hack/Cargo.toml +++ b/src/tools/rustc-workspace-hack/Cargo.toml @@ -61,6 +61,7 @@ features = [ "wincrypt", "windef", "winioctl", + "winnt", "winreg", "winsock2", "winuser",