@@ -6,7 +6,7 @@ use crate::ast::{
6
6
self , Param , BinOpKind , BindingMode , BlockCheckMode , Expr , ExprKind , Ident , Item , ItemKind ,
7
7
Mutability , Pat , PatKind , PathSegment , QSelf , Ty , TyKind ,
8
8
} ;
9
- use crate :: parse:: token:: { self , TokenKind } ;
9
+ use crate :: parse:: token:: { self , TokenKind , token_can_begin_expr } ;
10
10
use crate :: print:: pprust;
11
11
use crate :: ptr:: P ;
12
12
use crate :: symbol:: { kw, sym} ;
@@ -326,34 +326,8 @@ impl<'a> Parser<'a> {
326
326
}
327
327
}
328
328
329
- let is_semi_suggestable = expected. iter ( ) . any ( |t| match t {
330
- TokenType :: Token ( token:: Semi ) => true , // We expect a `;` here.
331
- _ => false ,
332
- } ) && ( // A `;` would be expected before the current keyword.
333
- self . token . is_keyword ( kw:: Break ) ||
334
- self . token . is_keyword ( kw:: Continue ) ||
335
- self . token . is_keyword ( kw:: For ) ||
336
- self . token . is_keyword ( kw:: If ) ||
337
- self . token . is_keyword ( kw:: Let ) ||
338
- self . token . is_keyword ( kw:: Loop ) ||
339
- self . token . is_keyword ( kw:: Match ) ||
340
- self . token . is_keyword ( kw:: Return ) ||
341
- self . token . is_keyword ( kw:: While )
342
- ) ;
343
329
let sm = self . sess . source_map ( ) ;
344
330
match ( sm. lookup_line ( self . token . span . lo ( ) ) , sm. lookup_line ( sp. lo ( ) ) ) {
345
- ( Ok ( ref a) , Ok ( ref b) ) if a. line != b. line && is_semi_suggestable => {
346
- // The spans are in different lines, expected `;` and found `let` or `return`.
347
- // High likelihood that it is only a missing `;`.
348
- err. span_suggestion_short (
349
- label_sp,
350
- "a semicolon may be missing here" ,
351
- ";" . to_string ( ) ,
352
- Applicability :: MaybeIncorrect ,
353
- ) ;
354
- err. emit ( ) ;
355
- return Ok ( true ) ;
356
- }
357
331
( Ok ( ref a) , Ok ( ref b) ) if a. line == b. line => {
358
332
// When the spans are in the same line, it means that the only content between
359
333
// them is whitespace, point at the found token in that case:
@@ -902,18 +876,61 @@ impl<'a> Parser<'a> {
902
876
}
903
877
}
904
878
let sm = self . sess . source_map ( ) ;
905
- match ( sm. lookup_line ( prev_sp. lo ( ) ) , sm. lookup_line ( sp. lo ( ) ) ) {
906
- ( Ok ( ref a) , Ok ( ref b) ) if a. line == b. line => {
907
- // When the spans are in the same line, it means that the only content
908
- // between them is whitespace, point only at the found token.
909
- err. span_label ( sp, label_exp) ;
879
+ if !sm. is_multiline ( prev_sp. until ( sp) ) {
880
+ // When the spans are in the same line, it means that the only content
881
+ // between them is whitespace, point only at the found token.
882
+ err. span_label ( sp, label_exp) ;
883
+ } else {
884
+ err. span_label ( prev_sp, label_exp) ;
885
+ err. span_label ( sp, "unexpected token" ) ;
886
+ }
887
+ Err ( err)
888
+ }
889
+
890
+ pub ( super ) fn expect_semi ( & mut self ) -> PResult < ' a , ( ) > {
891
+ if self . eat ( & token:: Semi ) {
892
+ return Ok ( ( ) ) ;
893
+ }
894
+ let sm = self . sess . source_map ( ) ;
895
+ let msg = format ! ( "expected `;`, found `{}`" , self . this_token_descr( ) ) ;
896
+ let appl = Applicability :: MachineApplicable ;
897
+ if self . look_ahead ( 1 , |t| t == & token:: CloseDelim ( token:: Brace )
898
+ || token_can_begin_expr ( t) && t. kind != token:: Colon
899
+ ) && [ token:: Comma , token:: Colon ] . contains ( & self . token . kind ) {
900
+ // Likely typo: `,` → `;` or `:` → `;`. This is triggered if the current token is
901
+ // either `,` or `:`, and the next token could either start a new statement or is a
902
+ // block close. For example:
903
+ //
904
+ // let x = 32:
905
+ // let y = 42;
906
+ if sm. is_multiline ( self . prev_span . until ( self . token . span ) ) {
907
+ self . bump ( ) ;
908
+ let sp = self . prev_span ;
909
+ self . struct_span_err ( sp, & msg)
910
+ . span_suggestion ( sp, "change this to `;`" , ";" . to_string ( ) , appl)
911
+ . emit ( ) ;
912
+ return Ok ( ( ) )
910
913
}
911
- _ => {
912
- err. span_label ( prev_sp, label_exp) ;
913
- err. span_label ( sp, "unexpected token" ) ;
914
+ } else if self . look_ahead ( 0 , |t| t == & token:: CloseDelim ( token:: Brace ) || (
915
+ token_can_begin_expr ( t)
916
+ && t != & token:: Semi
917
+ && t != & token:: Pound // Avoid triggering with too many trailing `#` in raw string.
918
+ ) ) {
919
+ // Missing semicolon typo. This is triggered if the next token could either start a
920
+ // new statement or is a block close. For example:
921
+ //
922
+ // let x = 32
923
+ // let y = 42;
924
+ if sm. is_multiline ( self . prev_span . until ( self . token . span ) ) {
925
+ let sp = self . prev_span . shrink_to_hi ( ) ;
926
+ self . struct_span_err ( sp, & msg)
927
+ . span_label ( self . token . span , "unexpected token" )
928
+ . span_suggestion_short ( sp, "add `;` here" , ";" . to_string ( ) , appl)
929
+ . emit ( ) ;
930
+ return Ok ( ( ) )
914
931
}
915
932
}
916
- Err ( err )
933
+ self . expect ( & token :: Semi ) . map ( |_| ( ) ) // Error unconditionally
917
934
}
918
935
919
936
pub ( super ) fn parse_semi_or_incorrect_foreign_fn_body (
@@ -943,7 +960,7 @@ impl<'a> Parser<'a> {
943
960
Err ( mut err) => {
944
961
err. cancel ( ) ;
945
962
mem:: replace ( self , parser_snapshot) ;
946
- self . expect ( & token :: Semi ) ?;
963
+ self . expect_semi ( ) ?;
947
964
}
948
965
}
949
966
} else {
0 commit comments