diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs index c3fc09343dbf..c9723800b36e 100644 --- a/clippy_lints/src/equatable_if_let.rs +++ b/clippy_lints/src/equatable_if_let.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::implements_trait; use rustc_errors::Applicability; @@ -40,9 +40,9 @@ declare_clippy_lint! { declare_lint_pass!(PatternEquality => [EQUATABLE_IF_LET]); /// detects if pattern matches just one thing -fn unary_pattern(pat: &Pat<'_>) -> bool { +fn is_unary_pattern(pat: &Pat<'_>) -> bool { fn array_rec(pats: &[Pat<'_>]) -> bool { - pats.iter().all(unary_pattern) + pats.iter().all(is_unary_pattern) } match &pat.kind { PatKind::Missing => unreachable!(), @@ -53,9 +53,9 @@ fn unary_pattern(pat: &Pat<'_>) -> bool { | PatKind::Never | PatKind::Or(_) | PatKind::Err(_) => false, - PatKind::Struct(_, a, etc) => etc.is_none() && a.iter().all(|x| unary_pattern(x.pat)), + PatKind::Struct(_, a, etc) => etc.is_none() && a.iter().all(|x| is_unary_pattern(x.pat)), PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a), - PatKind::Ref(x, _) | PatKind::Box(x) | PatKind::Deref(x) | PatKind::Guard(x, _) => unary_pattern(x), + PatKind::Ref(x, _) | PatKind::Box(x) | PatKind::Deref(x) | PatKind::Guard(x, _) => is_unary_pattern(x), PatKind::Expr(_) => true, } } @@ -103,48 +103,53 @@ fn contains_type_mismatch(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { impl<'tcx> LateLintPass<'tcx> for PatternEquality { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let ExprKind::Let(let_expr) = expr.kind - && unary_pattern(let_expr.pat) - && !expr.span.in_external_macro(cx.sess().source_map()) + && is_unary_pattern(let_expr.pat) + && !let_expr.pat.span.from_expansion() + && !let_expr.init.span.from_expansion() { let exp_ty = cx.typeck_results().expr_ty(let_expr.init); let pat_ty = cx.typeck_results().pat_ty(let_expr.pat); - let mut applicability = Applicability::MachineApplicable; + + let mut app = Applicability::MachineApplicable; + let ctxt = expr.span.ctxt(); if is_structural_partial_eq(cx, exp_ty, pat_ty) && !contains_type_mismatch(cx, let_expr.pat) { - let pat_str = match let_expr.pat.kind { - PatKind::Struct(..) => format!( - "({})", - snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0, - ), - _ => snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability) - .0 - .to_string(), - }; - span_lint_and_sugg( + span_lint_and_then( cx, EQUATABLE_IF_LET, expr.span, "this pattern matching can be expressed using equality", - "try", - format!( - "{} == {pat_str}", - snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0, - ), - applicability, + |diag| { + let pat_str = { + let str = snippet_with_context(cx, let_expr.pat.span, ctxt, "..", &mut app).0; + if let PatKind::Struct(..) = let_expr.pat.kind { + format!("({str})").into() + } else { + str + } + }; + + let sugg = format!( + "{} == {pat_str}", + snippet_with_context(cx, let_expr.init.span, ctxt, "..", &mut app).0, + ); + diag.span_suggestion(expr.span, "try", sugg, app); + }, ); } else { - span_lint_and_sugg( + span_lint_and_then( cx, EQUATABLE_IF_LET, expr.span, "this pattern matching can be expressed using `matches!`", - "try", - format!( - "matches!({}, {})", - snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0, - snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0, - ), - applicability, + |diag| { + let sugg = format!( + "matches!({}, {})", + snippet_with_context(cx, let_expr.init.span, ctxt, "..", &mut app).0, + snippet_with_context(cx, let_expr.pat.span, ctxt, "..", &mut app).0, + ); + diag.span_suggestion(expr.span, "try", sugg, app); + }, ); } } diff --git a/tests/ui/equatable_if_let.fixed b/tests/ui/equatable_if_let.fixed index 58fbad64a78d..a8466710796a 100644 --- a/tests/ui/equatable_if_let.fixed +++ b/tests/ui/equatable_if_let.fixed @@ -1,14 +1,6 @@ //@aux-build:proc_macros.rs - -#![allow( - unused_variables, - dead_code, - clippy::derive_partial_eq_without_eq, - clippy::needless_ifs -)] +#![allow(clippy::derive_partial_eq_without_eq, clippy::needless_ifs)] #![warn(clippy::equatable_if_let)] - -extern crate proc_macros; use proc_macros::{external, inline_macros}; use std::cmp::Ordering; @@ -48,7 +40,6 @@ impl PartialEq for NotStructuralEq { } } -#[inline_macros] fn main() { let a = 2; let b = 3; @@ -95,13 +86,6 @@ fn main() { //~^ equatable_if_let if matches!(h, NoPartialEqStruct { a: 2, b: false }) {} //~^ equatable_if_let - - if "abc" == inline!("abc") { - //~^ equatable_if_let - println!("OK"); - } - - external!({ if let 2 = $a {} }); } mod issue8710 { @@ -139,3 +123,39 @@ mod issue8710 { } } } + +#[inline_macros] +fn issue14548() { + if let inline!("abc") = "abc" { + println!("OK"); + } + + let a = 2; + external!({ if let 2 = $a {} }); + + macro_rules! generate_script { + ($($($font:literal)|*),+) => { + pub fn script(font: &str) { + $( + if let $($font)|* = font {} + )* + } + } + } + + generate_script! { + "Sans" | "Serif" | "Sans Mono", + "Znamenny Musical Notation" + } + + macro_rules! generate_script2 { + ($($font:literal)|*) => {{ + pub fn script2(font: &str) { + if let $($font)|* = font {} + } + }} + } + + generate_script2!("Sans" | "Serif" | "Sans Mono"); + generate_script2!("Znamenny Musical Notation"); +} diff --git a/tests/ui/equatable_if_let.rs b/tests/ui/equatable_if_let.rs index cca97c76b509..38a95816a42b 100644 --- a/tests/ui/equatable_if_let.rs +++ b/tests/ui/equatable_if_let.rs @@ -1,14 +1,6 @@ //@aux-build:proc_macros.rs - -#![allow( - unused_variables, - dead_code, - clippy::derive_partial_eq_without_eq, - clippy::needless_ifs -)] +#![allow(clippy::derive_partial_eq_without_eq, clippy::needless_ifs)] #![warn(clippy::equatable_if_let)] - -extern crate proc_macros; use proc_macros::{external, inline_macros}; use std::cmp::Ordering; @@ -48,7 +40,6 @@ impl PartialEq for NotStructuralEq { } } -#[inline_macros] fn main() { let a = 2; let b = 3; @@ -95,13 +86,6 @@ fn main() { //~^ equatable_if_let if let NoPartialEqStruct { a: 2, b: false } = h {} //~^ equatable_if_let - - if let inline!("abc") = "abc" { - //~^ equatable_if_let - println!("OK"); - } - - external!({ if let 2 = $a {} }); } mod issue8710 { @@ -139,3 +123,39 @@ mod issue8710 { } } } + +#[inline_macros] +fn issue14548() { + if let inline!("abc") = "abc" { + println!("OK"); + } + + let a = 2; + external!({ if let 2 = $a {} }); + + macro_rules! generate_script { + ($($($font:literal)|*),+) => { + pub fn script(font: &str) { + $( + if let $($font)|* = font {} + )* + } + } + } + + generate_script! { + "Sans" | "Serif" | "Sans Mono", + "Znamenny Musical Notation" + } + + macro_rules! generate_script2 { + ($($font:literal)|*) => {{ + pub fn script2(font: &str) { + if let $($font)|* = font {} + } + }} + } + + generate_script2!("Sans" | "Serif" | "Sans Mono"); + generate_script2!("Znamenny Musical Notation"); +} diff --git a/tests/ui/equatable_if_let.stderr b/tests/ui/equatable_if_let.stderr index dd1832ad68b2..a6fb10ddc334 100644 --- a/tests/ui/equatable_if_let.stderr +++ b/tests/ui/equatable_if_let.stderr @@ -1,5 +1,5 @@ error: this pattern matching can be expressed using equality - --> tests/ui/equatable_if_let.rs:64:8 + --> tests/ui/equatable_if_let.rs:55:8 | LL | if let 2 = a {} | ^^^^^^^^^ help: try: `a == 2` @@ -8,100 +8,94 @@ LL | if let 2 = a {} = help: to override `-D warnings` add `#[allow(clippy::equatable_if_let)]` error: this pattern matching can be expressed using equality - --> tests/ui/equatable_if_let.rs:66:8 + --> tests/ui/equatable_if_let.rs:57:8 | LL | if let Ordering::Greater = a.cmp(&b) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.cmp(&b) == Ordering::Greater` error: this pattern matching can be expressed using equality - --> tests/ui/equatable_if_let.rs:68:8 + --> tests/ui/equatable_if_let.rs:59:8 | LL | if let Some(2) = c {} | ^^^^^^^^^^^^^^^ help: try: `c == Some(2)` error: this pattern matching can be expressed using equality - --> tests/ui/equatable_if_let.rs:70:8 + --> tests/ui/equatable_if_let.rs:61:8 | LL | if let Struct { a: 2, b: false } = d {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `d == (Struct { a: 2, b: false })` error: this pattern matching can be expressed using equality - --> tests/ui/equatable_if_let.rs:72:8 + --> tests/ui/equatable_if_let.rs:63:8 | LL | if let Enum::TupleVariant(32, 64) = e {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::TupleVariant(32, 64)` error: this pattern matching can be expressed using equality - --> tests/ui/equatable_if_let.rs:74:8 + --> tests/ui/equatable_if_let.rs:65:8 | LL | if let Enum::RecordVariant { a: 64, b: 32 } = e {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == (Enum::RecordVariant { a: 64, b: 32 })` error: this pattern matching can be expressed using equality - --> tests/ui/equatable_if_let.rs:76:8 + --> tests/ui/equatable_if_let.rs:67:8 | LL | if let Enum::UnitVariant = e {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::UnitVariant` error: this pattern matching can be expressed using equality - --> tests/ui/equatable_if_let.rs:78:8 + --> tests/ui/equatable_if_let.rs:69:8 | LL | if let (Enum::UnitVariant, &Struct { a: 2, b: false }) = (e, &d) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(e, &d) == (Enum::UnitVariant, &Struct { a: 2, b: false })` error: this pattern matching can be expressed using `matches!` - --> tests/ui/equatable_if_let.rs:88:8 + --> tests/ui/equatable_if_let.rs:79:8 | LL | if let NotPartialEq::A = f {} | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(f, NotPartialEq::A)` error: this pattern matching can be expressed using equality - --> tests/ui/equatable_if_let.rs:90:8 + --> tests/ui/equatable_if_let.rs:81:8 | LL | if let NotStructuralEq::A = g {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `g == NotStructuralEq::A` error: this pattern matching can be expressed using `matches!` - --> tests/ui/equatable_if_let.rs:92:8 + --> tests/ui/equatable_if_let.rs:83:8 | LL | if let Some(NotPartialEq::A) = Some(f) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(Some(f), Some(NotPartialEq::A))` error: this pattern matching can be expressed using equality - --> tests/ui/equatable_if_let.rs:94:8 + --> tests/ui/equatable_if_let.rs:85:8 | LL | if let Some(NotStructuralEq::A) = Some(g) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(g) == Some(NotStructuralEq::A)` error: this pattern matching can be expressed using `matches!` - --> tests/ui/equatable_if_let.rs:96:8 + --> tests/ui/equatable_if_let.rs:87:8 | LL | if let NoPartialEqStruct { a: 2, b: false } = h {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(h, NoPartialEqStruct { a: 2, b: false })` -error: this pattern matching can be expressed using equality - --> tests/ui/equatable_if_let.rs:99:8 - | -LL | if let inline!("abc") = "abc" { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"abc" == inline!("abc")` - error: this pattern matching can be expressed using `matches!` - --> tests/ui/equatable_if_let.rs:109:12 + --> tests/ui/equatable_if_let.rs:93:12 | LL | if let Some('i') = cs.iter().next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(cs.iter().next(), Some('i'))` error: this pattern matching can be expressed using `matches!` - --> tests/ui/equatable_if_let.rs:117:12 + --> tests/ui/equatable_if_let.rs:101:12 | LL | if let Some(1) = cs.iter().next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(cs.iter().next(), Some(1))` error: this pattern matching can be expressed using `matches!` - --> tests/ui/equatable_if_let.rs:135:12 + --> tests/ui/equatable_if_let.rs:119:12 | LL | if let Some(MyEnum::B) = get_enum() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(get_enum(), Some(MyEnum::B))` -error: aborting due to 17 previous errors +error: aborting due to 16 previous errors