@@ -7,7 +7,8 @@ use super::{PatCtxt, PatternError};
77use rustc_arena:: TypedArena ;
88use rustc_ast:: Mutability ;
99use rustc_errors:: {
10- error_code, struct_span_err, Applicability , Diagnostic , DiagnosticBuilder , ErrorGuaranteed ,
10+ error_code, pluralize, struct_span_err, Applicability , Diagnostic , DiagnosticBuilder ,
11+ ErrorGuaranteed ,
1112} ;
1213use rustc_hir as hir;
1314use rustc_hir:: def:: * ;
@@ -20,7 +21,7 @@ use rustc_session::lint::builtin::{
2021} ;
2122use rustc_session:: Session ;
2223use rustc_span:: source_map:: Spanned ;
23- use rustc_span:: { DesugaringKind , ExpnKind , MultiSpan , Span } ;
24+ use rustc_span:: { BytePos , DesugaringKind , ExpnKind , MultiSpan , Span } ;
2425
2526crate fn check_match ( tcx : TyCtxt < ' _ > , def_id : DefId ) {
2627 let body_id = match def_id. as_local ( ) {
@@ -241,6 +242,9 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
241242 }
242243
243244 let joined_patterns = joined_uncovered_patterns ( & cx, & witnesses) ;
245+
246+ let mut bindings = vec ! [ ] ;
247+
244248 let mut err = struct_span_err ! (
245249 self . tcx. sess,
246250 pat. span,
@@ -257,6 +261,16 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
257261 false
258262 }
259263 _ => {
264+ pat. walk ( & mut |pat : & hir:: Pat < ' _ > | {
265+ match pat. kind {
266+ hir:: PatKind :: Binding ( _, _, ident, _) => {
267+ bindings. push ( ident) ;
268+ }
269+ _ => { }
270+ }
271+ true
272+ } ) ;
273+
260274 err. span_label ( pat. span , pattern_not_covered_label ( & witnesses, & joined_patterns) ) ;
261275 true
262276 }
@@ -267,13 +281,71 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
267281 "`let` bindings require an \" irrefutable pattern\" , like a `struct` or \
268282 an `enum` with only one variant",
269283 ) ;
270- if let Ok ( snippet) = self . tcx . sess . source_map ( ) . span_to_snippet ( span) {
271- err. span_suggestion (
272- span,
273- "you might want to use `if let` to ignore the variant that isn't matched" ,
274- format ! ( "if {} {{ /* */ }}" , & snippet[ ..snippet. len( ) - 1 ] ) ,
284+ if self . tcx . sess . source_map ( ) . span_to_snippet ( span) . is_ok ( ) {
285+ let semi_span = span. shrink_to_hi ( ) . with_lo ( span. hi ( ) - BytePos ( 1 ) ) ;
286+ let start_span = span. shrink_to_lo ( ) ;
287+ let end_span = semi_span. shrink_to_lo ( ) ;
288+ err. multipart_suggestion (
289+ & format ! (
290+ "you might want to use `if let` to ignore the variant{} that {} matched" ,
291+ pluralize!( witnesses. len( ) ) ,
292+ match witnesses. len( ) {
293+ 1 => "isn't" ,
294+ _ => "aren't" ,
295+ } ,
296+ ) ,
297+ vec ! [
298+ match & bindings[ ..] {
299+ [ ] => ( start_span, "if " . to_string( ) ) ,
300+ [ binding] => ( start_span, format!( "let {} = if " , binding) ) ,
301+ bindings => (
302+ start_span,
303+ format!(
304+ "let ({}) = if " ,
305+ bindings
306+ . iter( )
307+ . map( |ident| ident. to_string( ) )
308+ . collect:: <Vec <_>>( )
309+ . join( ", " )
310+ ) ,
311+ ) ,
312+ } ,
313+ match & bindings[ ..] {
314+ [ ] => ( semi_span, " { todo!() }" . to_string( ) ) ,
315+ [ binding] => {
316+ ( end_span, format!( " {{ {} }} else {{ todo!() }}" , binding) )
317+ }
318+ bindings => (
319+ end_span,
320+ format!(
321+ " {{ ({}) }} else {{ todo!() }}" ,
322+ bindings
323+ . iter( )
324+ . map( |ident| ident. to_string( ) )
325+ . collect:: <Vec <_>>( )
326+ . join( ", " )
327+ ) ,
328+ ) ,
329+ } ,
330+ ] ,
275331 Applicability :: HasPlaceholders ,
276332 ) ;
333+ if !bindings. is_empty ( ) && cx. tcx . sess . is_nightly_build ( ) {
334+ err. span_suggestion_verbose (
335+ semi_span. shrink_to_lo ( ) ,
336+ & format ! (
337+ "alternatively, on nightly, you might want to use \
338+ `#![feature(let_else)]` to handle the variant{} that {} matched",
339+ pluralize!( witnesses. len( ) ) ,
340+ match witnesses. len( ) {
341+ 1 => "isn't" ,
342+ _ => "aren't" ,
343+ } ,
344+ ) ,
345+ " else { todo!() }" . to_string ( ) ,
346+ Applicability :: HasPlaceholders ,
347+ ) ;
348+ }
277349 }
278350 err. note (
279351 "for more information, visit \
0 commit comments