@@ -6,7 +6,7 @@ use crate::ast::{
66 self , Param , BinOpKind , BindingMode , BlockCheckMode , Expr , ExprKind , Ident , Item , ItemKind ,
77 Mutability , Pat , PatKind , PathSegment , QSelf , Ty , TyKind ,
88} ;
9- use crate :: parse:: token:: { self , TokenKind } ;
9+ use crate :: parse:: token:: { self , TokenKind , token_can_begin_expr } ;
1010use crate :: print:: pprust;
1111use crate :: ptr:: P ;
1212use crate :: symbol:: { kw, sym} ;
@@ -326,34 +326,8 @@ impl<'a> Parser<'a> {
326326 }
327327 }
328328
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- ) ;
343329 let sm = self . sess . source_map ( ) ;
344330 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- }
357331 ( Ok ( ref a) , Ok ( ref b) ) if a. line == b. line => {
358332 // When the spans are in the same line, it means that the only content between
359333 // them is whitespace, point at the found token in that case:
@@ -902,18 +876,61 @@ impl<'a> Parser<'a> {
902876 }
903877 }
904878 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 ( ( ) )
910913 }
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 ( ( ) )
914931 }
915932 }
916- Err ( err )
933+ self . expect ( & token :: Semi ) . map ( |_| ( ) ) // Error unconditionally
917934 }
918935
919936 pub ( super ) fn parse_semi_or_incorrect_foreign_fn_body (
@@ -943,7 +960,7 @@ impl<'a> Parser<'a> {
943960 Err ( mut err) => {
944961 err. cancel ( ) ;
945962 mem:: replace ( self , parser_snapshot) ;
946- self . expect ( & token :: Semi ) ?;
963+ self . expect_semi ( ) ?;
947964 }
948965 }
949966 } else {
0 commit comments