From 1babece1e8888151b1b248367eb1017450dc107c Mon Sep 17 00:00:00 2001 From: Ezra Shaw Date: Fri, 13 Jan 2023 14:14:26 +1300 Subject: [PATCH] suggest fix for attempted integer identifier in patterns --- .../locales/en-US/mir_build.ftl | 2 ++ compiler/rustc_mir_build/src/errors.rs | 15 ++++++++ .../src/thir/pattern/check_match.rs | 20 ++++++++--- .../ui/consts/const-match-check.eval1.stderr | 4 +++ .../ui/consts/const-match-check.eval2.stderr | 4 +++ .../consts/const-match-check.matchck.stderr | 16 +++++++++ tests/ui/pattern/issue-106552.rs | 7 ++++ tests/ui/pattern/issue-106552.stderr | 35 +++++++++++++++++++ 8 files changed, 99 insertions(+), 4 deletions(-) create mode 100644 tests/ui/pattern/issue-106552.rs create mode 100644 tests/ui/pattern/issue-106552.stderr diff --git a/compiler/rustc_error_messages/locales/en-US/mir_build.ftl b/compiler/rustc_error_messages/locales/en-US/mir_build.ftl index a082c0b61fa..224855fff8b 100644 --- a/compiler/rustc_error_messages/locales/en-US/mir_build.ftl +++ b/compiler/rustc_error_messages/locales/en-US/mir_build.ftl @@ -364,3 +364,5 @@ mir_build_suggest_let_else = you might want to use `let else` to handle the {$co [one] variant that isn't *[other] variants that aren't } matched + +mir_build_suggest_attempted_int_lit = alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 06523b0a1de..7f81aef1c73 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -770,6 +770,8 @@ pub(crate) struct PatternNotCovered<'s, 'tcx> { #[subdiagnostic] pub let_suggestion: Option, #[subdiagnostic] + pub misc_suggestion: Option, + #[subdiagnostic] pub res_defined_here: Option, } @@ -848,3 +850,16 @@ pub enum SuggestLet { count: usize, }, } + +#[derive(Subdiagnostic)] +pub enum MiscPatternSuggestion { + #[suggestion( + mir_build_suggest_attempted_int_lit, + code = "_", + applicability = "maybe-incorrect" + )] + AttemptedIntegerLiteral { + #[primary_span] + start_span: Span, + }, +} diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index e13c0662ef8..34e637f5948 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -6,8 +6,9 @@ use super::{PatCtxt, PatternError}; use crate::errors::*; +use hir::{ExprKind, PatKind}; use rustc_arena::TypedArena; -use rustc_ast::Mutability; +use rustc_ast::{LitKind, Mutability}; use rustc_errors::{ struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, }; @@ -389,7 +390,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { return; } - let (inform, interpreted_as_const, res_defined_here,let_suggestion) = + let (inform, interpreted_as_const, res_defined_here,let_suggestion, misc_suggestion) = if let hir::PatKind::Path(hir::QPath::Resolved( None, hir::Path { @@ -413,6 +414,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { } }, None, + None, ) } else if let Some(span) = sp && self.tcx.sess.source_map().is_span_accessible(span) { let mut bindings = vec![]; @@ -426,10 +428,19 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { let end_span = semi_span.shrink_to_lo(); let count = witnesses.len(); + // If the pattern to match is an integer literal: + let int_suggestion = if + let PatKind::Lit(expr) = &pat.kind + && bindings.is_empty() + && let ExprKind::Lit(Spanned { node: LitKind::Int(_, _), span }) = expr.kind { + // Then give a suggestion, the user might've meant to create a binding instead. + Some(MiscPatternSuggestion::AttemptedIntegerLiteral { start_span: span.shrink_to_lo() }) + } else { None }; + let let_suggestion = if bindings.is_empty() {SuggestLet::If{start_span, semi_span, count}} else{ SuggestLet::Else{end_span, count }}; - (sp.map(|_|Inform), None, None, Some(let_suggestion)) + (sp.map(|_|Inform), None, None, Some(let_suggestion), int_suggestion) } else{ - (sp.map(|_|Inform), None, None, None) + (sp.map(|_|Inform), None, None, None, None) }; let adt_defined_here = try { @@ -453,6 +464,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { _p: (), pattern_ty, let_suggestion, + misc_suggestion, res_defined_here, adt_defined_here, }); diff --git a/tests/ui/consts/const-match-check.eval1.stderr b/tests/ui/consts/const-match-check.eval1.stderr index 1caf1617e21..08fcd1deab1 100644 --- a/tests/ui/consts/const-match-check.eval1.stderr +++ b/tests/ui/consts/const-match-check.eval1.stderr @@ -11,6 +11,10 @@ help: you might want to use `if let` to ignore the variants that aren't matched | LL | A = { if let 0 = 0 { todo!() } 0 }, | ++ ~~~~~~~~~~~ +help: alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits + | +LL | A = { let _0 = 0; 0 }, + | + error: aborting due to previous error diff --git a/tests/ui/consts/const-match-check.eval2.stderr b/tests/ui/consts/const-match-check.eval2.stderr index f038ba1c8ed..5d86ca4bfd1 100644 --- a/tests/ui/consts/const-match-check.eval2.stderr +++ b/tests/ui/consts/const-match-check.eval2.stderr @@ -11,6 +11,10 @@ help: you might want to use `if let` to ignore the variants that aren't matched | LL | let x: [i32; { if let 0 = 0 { todo!() } 0 }] = []; | ++ ~~~~~~~~~~~ +help: alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits + | +LL | let x: [i32; { let _0 = 0; 0 }] = []; + | + error: aborting due to previous error diff --git a/tests/ui/consts/const-match-check.matchck.stderr b/tests/ui/consts/const-match-check.matchck.stderr index b1921f8a41e..c8f66bb0fc0 100644 --- a/tests/ui/consts/const-match-check.matchck.stderr +++ b/tests/ui/consts/const-match-check.matchck.stderr @@ -11,6 +11,10 @@ help: you might want to use `if let` to ignore the variants that aren't matched | LL | const X: i32 = { if let 0 = 0 { todo!() } 0 }; | ++ ~~~~~~~~~~~ +help: alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits + | +LL | const X: i32 = { let _0 = 0; 0 }; + | + error[E0005]: refutable pattern in local binding --> $DIR/const-match-check.rs:8:23 @@ -25,6 +29,10 @@ help: you might want to use `if let` to ignore the variants that aren't matched | LL | static Y: i32 = { if let 0 = 0 { todo!() } 0 }; | ++ ~~~~~~~~~~~ +help: alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits + | +LL | static Y: i32 = { let _0 = 0; 0 }; + | + error[E0005]: refutable pattern in local binding --> $DIR/const-match-check.rs:13:26 @@ -39,6 +47,10 @@ help: you might want to use `if let` to ignore the variants that aren't matched | LL | const X: i32 = { if let 0 = 0 { todo!() } 0 }; | ++ ~~~~~~~~~~~ +help: alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits + | +LL | const X: i32 = { let _0 = 0; 0 }; + | + error[E0005]: refutable pattern in local binding --> $DIR/const-match-check.rs:19:26 @@ -53,6 +65,10 @@ help: you might want to use `if let` to ignore the variants that aren't matched | LL | const X: i32 = { if let 0 = 0 { todo!() } 0 }; | ++ ~~~~~~~~~~~ +help: alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits + | +LL | const X: i32 = { let _0 = 0; 0 }; + | + error: aborting due to 4 previous errors diff --git a/tests/ui/pattern/issue-106552.rs b/tests/ui/pattern/issue-106552.rs new file mode 100644 index 00000000000..aa2c141e05e --- /dev/null +++ b/tests/ui/pattern/issue-106552.rs @@ -0,0 +1,7 @@ +fn main() { + let 5 = 6; + //~^ error refutable pattern in local binding [E0005] + + let x @ 5 = 6; + //~^ error refutable pattern in local binding [E0005] +} diff --git a/tests/ui/pattern/issue-106552.stderr b/tests/ui/pattern/issue-106552.stderr new file mode 100644 index 00000000000..ed5d40c0968 --- /dev/null +++ b/tests/ui/pattern/issue-106552.stderr @@ -0,0 +1,35 @@ +error[E0005]: refutable pattern in local binding + --> $DIR/issue-106552.rs:2:9 + | +LL | let 5 = 6; + | ^ patterns `i32::MIN..=4_i32` and `6_i32..=i32::MAX` not covered + | + = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant + = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `i32` +help: you might want to use `if let` to ignore the variants that aren't matched + | +LL | if let 5 = 6 { todo!() } + | ++ ~~~~~~~~~~~ +help: alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits + | +LL | let _5 = 6; + | + + +error[E0005]: refutable pattern in local binding + --> $DIR/issue-106552.rs:5:9 + | +LL | let x @ 5 = 6; + | ^^^^^ patterns `i32::MIN..=4_i32` and `6_i32..=i32::MAX` not covered + | + = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant + = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `i32` +help: you might want to use `let else` to handle the variants that aren't matched + | +LL | let x @ 5 = 6 else { todo!() }; + | ++++++++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0005`.