From 1d930483247bac3dd7df3470abd5432d198fe755 Mon Sep 17 00:00:00 2001 From: Gus Wynn Date: Tue, 15 Sep 2020 09:56:17 -0700 Subject: [PATCH 1/2] give better suggestion when matching a const range --- compiler/rustc_typeck/src/check/pat.rs | 53 +++++++++++++++++++++----- src/test/ui/issues/issue-76191.rs | 5 +++ src/test/ui/issues/issue-76191.stderr | 26 ++++++++++++- 3 files changed, 73 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index 6be2fdf9f19..3b1559dfcf5 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -772,12 +772,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } _ => { - let const_def_id = match pat_ty.kind() { + let (type_def_id, item_def_id) = match pat_ty.kind() { Adt(def, _) => match res { - Res::Def(DefKind::Const, _) => Some(def.did), - _ => None, + Res::Def(DefKind::Const, def_id) => (Some(def.did), Some(def_id)), + _ => (None, None), }, - _ => None, + _ => (None, None), }; let ranges = &[ @@ -788,11 +788,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.lang_items().range_inclusive_struct(), self.tcx.lang_items().range_to_inclusive_struct(), ]; - if const_def_id != None && ranges.contains(&const_def_id) { - let msg = "constants only support matching by type, \ - if you meant to match against a range of values, \ - consider using a range pattern like `min ..= max` in the match block"; - e.note(msg); + if type_def_id != None && ranges.contains(&type_def_id) { + let generic_message = match item_def_id { + Some(def_id) => match self.tcx.hir().get_if_local(def_id) { + Some(hir::Node::Item(hir::Item { + kind: hir::ItemKind::Const(_, body_id), + .. + })) => match self.tcx.hir().get(body_id.hir_id) { + hir::Node::Expr(expr) => { + if hir::is_range_literal(expr) { + let span = self.tcx.hir().span(body_id.hir_id); + if let Ok(snip) = + self.tcx.sess.source_map().span_to_snippet(span) + { + e.span_suggestion_verbose( + span, + "you may want to move the range into the match block", + snip, + Applicability::MachineApplicable + ); + false + } else { + true + } + } else { + true + } + } + _ => true, + }, + _ => true, + }, + _ => true, + }; + + if generic_message { + let msg = "constants only support matching by type, \ + if you meant to match against a range of values, \ + consider using a range pattern like `min ..= max` in the match block"; + e.note(msg); + } } else { let msg = "introduce a new binding instead"; let sugg = format!("other_{}", ident.as_str().to_lowercase()); diff --git a/src/test/ui/issues/issue-76191.rs b/src/test/ui/issues/issue-76191.rs index bc327123c6f..d9790d2b56e 100644 --- a/src/test/ui/issues/issue-76191.rs +++ b/src/test/ui/issues/issue-76191.rs @@ -2,13 +2,18 @@ #![allow(non_snake_case)] use std::ops::RangeInclusive; + const RANGE: RangeInclusive = 0..=255; +const RANGE2: RangeInclusive = panic!(); + fn main() { let n: i32 = 1; match n { RANGE => {} //~^ ERROR mismatched types + RANGE2 => {} + //~^ ERROR mismatched types _ => {} } } diff --git a/src/test/ui/issues/issue-76191.stderr b/src/test/ui/issues/issue-76191.stderr index a5544d9e9da..61f4be81f43 100644 --- a/src/test/ui/issues/issue-76191.stderr +++ b/src/test/ui/issues/issue-76191.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-76191.rs:10:9 + --> $DIR/issue-76191.rs:13:9 | LL | const RANGE: RangeInclusive = 0..=255; | ------------------------------------------- constant defined here @@ -14,8 +14,30 @@ LL | RANGE => {} | = note: expected type `i32` found struct `RangeInclusive` +help: you may want to move the range into the match block + | +LL | const RANGE: RangeInclusive = 0..=255; + | ^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/issue-76191.rs:15:9 + | +LL | const RANGE2: RangeInclusive = panic!(); + | --------------------------------------------- constant defined here +... +LL | match n { + | - this expression has type `i32` +... +LL | RANGE2 => {} + | ^^^^^^ + | | + | expected `i32`, found struct `RangeInclusive` + | `RANGE2` is interpreted as a constant, not a new binding + | + = note: expected type `i32` + found struct `RangeInclusive` = note: constants only support matching by type, if you meant to match against a range of values, consider using a range pattern like `min ..= max` in the match block -error: aborting due to previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0308`. From 230355f25f10306211a80040bec1561545715383 Mon Sep 17 00:00:00 2001 From: Gus Wynn Date: Wed, 16 Sep 2020 12:32:10 -0700 Subject: [PATCH 2/2] comments and factor to own method --- compiler/rustc_typeck/src/check/pat.rs | 70 +++++++++++++------------- src/test/ui/issues/issue-76191.stderr | 4 +- 2 files changed, 38 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index 3b1559dfcf5..713717cfe1a 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -1,5 +1,6 @@ use crate::check::FnCtxt; use rustc_ast as ast; + use rustc_ast::util::lev_distance::find_best_match_for_name; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder}; @@ -740,6 +741,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat_ty } + fn maybe_suggest_range_literal( + &self, + e: &mut DiagnosticBuilder<'_>, + opt_def_id: Option, + ident: Ident, + ) -> bool { + match opt_def_id { + Some(def_id) => match self.tcx.hir().get_if_local(def_id) { + Some(hir::Node::Item(hir::Item { + kind: hir::ItemKind::Const(_, body_id), .. + })) => match self.tcx.hir().get(body_id.hir_id) { + hir::Node::Expr(expr) => { + if hir::is_range_literal(expr) { + let span = self.tcx.hir().span(body_id.hir_id); + if let Ok(snip) = self.tcx.sess.source_map().span_to_snippet(span) { + e.span_suggestion_verbose( + ident.span, + "you may want to move the range into the match block", + snip, + Applicability::MachineApplicable, + ); + return true; + } + } + } + _ => (), + }, + _ => (), + }, + _ => (), + } + false + } + fn emit_bad_pat_path( &self, mut e: DiagnosticBuilder<'_>, @@ -789,40 +824,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.lang_items().range_to_inclusive_struct(), ]; if type_def_id != None && ranges.contains(&type_def_id) { - let generic_message = match item_def_id { - Some(def_id) => match self.tcx.hir().get_if_local(def_id) { - Some(hir::Node::Item(hir::Item { - kind: hir::ItemKind::Const(_, body_id), - .. - })) => match self.tcx.hir().get(body_id.hir_id) { - hir::Node::Expr(expr) => { - if hir::is_range_literal(expr) { - let span = self.tcx.hir().span(body_id.hir_id); - if let Ok(snip) = - self.tcx.sess.source_map().span_to_snippet(span) - { - e.span_suggestion_verbose( - span, - "you may want to move the range into the match block", - snip, - Applicability::MachineApplicable - ); - false - } else { - true - } - } else { - true - } - } - _ => true, - }, - _ => true, - }, - _ => true, - }; - - if generic_message { + if !self.maybe_suggest_range_literal(&mut e, item_def_id, *ident) { let msg = "constants only support matching by type, \ if you meant to match against a range of values, \ consider using a range pattern like `min ..= max` in the match block"; diff --git a/src/test/ui/issues/issue-76191.stderr b/src/test/ui/issues/issue-76191.stderr index 61f4be81f43..bdcd2fe1adc 100644 --- a/src/test/ui/issues/issue-76191.stderr +++ b/src/test/ui/issues/issue-76191.stderr @@ -16,8 +16,8 @@ LL | RANGE => {} found struct `RangeInclusive` help: you may want to move the range into the match block | -LL | const RANGE: RangeInclusive = 0..=255; - | ^^^^^^^ +LL | 0..=255 => {} + | ^^^^^^^ error[E0308]: mismatched types --> $DIR/issue-76191.rs:15:9