@@ -242,6 +242,63 @@ impl<'a> Parser<'a> {
242242 expected. sort_by_cached_key ( |x| x. to_string ( ) ) ;
243243 expected. dedup ( ) ;
244244
245+ let sm = self . sess . source_map ( ) ;
246+ let msg = format ! ( "expected `;`, found {}" , super :: token_descr( & self . token) ) ;
247+ let appl = Applicability :: MachineApplicable ;
248+ if expected. contains ( & TokenType :: Token ( token:: Semi ) ) {
249+ if self . token . span == DUMMY_SP || self . prev_token . span == DUMMY_SP {
250+ // Likely inside a macro, can't provide meaningful suggestions.
251+ } else if !sm. is_multiline ( self . prev_token . span . until ( self . token . span ) ) {
252+ // The current token is in the same line as the prior token, not recoverable.
253+ } else if [ token:: Comma , token:: Colon ] . contains ( & self . token . kind )
254+ && self . prev_token . kind == token:: CloseDelim ( token:: Paren )
255+ {
256+ // Likely typo: The current token is on a new line and is expected to be
257+ // `.`, `;`, `?`, or an operator after a close delimiter token.
258+ //
259+ // let a = std::process::Command::new("echo")
260+ // .arg("1")
261+ // ,arg("2")
262+ // ^
263+ // https://github.com/rust-lang/rust/issues/72253
264+ } else if self . look_ahead ( 1 , |t| {
265+ t == & token:: CloseDelim ( token:: Brace )
266+ || t. can_begin_expr ( ) && t. kind != token:: Colon
267+ } ) && [ token:: Comma , token:: Colon ] . contains ( & self . token . kind )
268+ {
269+ // Likely typo: `,` → `;` or `:` → `;`. This is triggered if the current token is
270+ // either `,` or `:`, and the next token could either start a new statement or is a
271+ // block close. For example:
272+ //
273+ // let x = 32:
274+ // let y = 42;
275+ self . bump ( ) ;
276+ let sp = self . prev_token . span ;
277+ self . struct_span_err ( sp, & msg)
278+ . span_suggestion_short ( sp, "change this to `;`" , ";" . to_string ( ) , appl)
279+ . emit ( ) ;
280+ return Ok ( false ) ;
281+ } else if self . look_ahead ( 0 , |t| {
282+ t == & token:: CloseDelim ( token:: Brace )
283+ || (
284+ t. can_begin_expr ( ) && t != & token:: Semi && t != & token:: Pound
285+ // Avoid triggering with too many trailing `#` in raw string.
286+ )
287+ } ) {
288+ // Missing semicolon typo. This is triggered if the next token could either start a
289+ // new statement or is a block close. For example:
290+ //
291+ // let x = 32
292+ // let y = 42;
293+ let sp = self . prev_token . span . shrink_to_hi ( ) ;
294+ self . struct_span_err ( sp, & msg)
295+ . span_label ( self . token . span , "unexpected token" )
296+ . span_suggestion_short ( sp, "add `;` here" , ";" . to_string ( ) , appl)
297+ . emit ( ) ;
298+ return Ok ( false ) ;
299+ }
300+ }
301+
245302 let expect = tokens_to_string ( & expected[ ..] ) ;
246303 let actual = super :: token_descr ( & self . token ) ;
247304 let ( msg_exp, ( label_sp, label_exp) ) = if expected. len ( ) > 1 {
@@ -303,7 +360,6 @@ impl<'a> Parser<'a> {
303360 return Err ( err) ;
304361 }
305362
306- let sm = self . sess . source_map ( ) ;
307363 if self . prev_token . span == DUMMY_SP {
308364 // Account for macro context where the previous span might not be
309365 // available to avoid incorrect output (#54841).
@@ -1144,62 +1200,6 @@ impl<'a> Parser<'a> {
11441200 if self . eat ( & token:: Semi ) {
11451201 return Ok ( ( ) ) ;
11461202 }
1147- let sm = self . sess . source_map ( ) ;
1148- let msg = format ! ( "expected `;`, found {}" , super :: token_descr( & self . token) ) ;
1149- let appl = Applicability :: MachineApplicable ;
1150- if self . token . span == DUMMY_SP || self . prev_token . span == DUMMY_SP {
1151- // Likely inside a macro, can't provide meaningful suggestions.
1152- return self . expect ( & token:: Semi ) . map ( drop) ;
1153- } else if !sm. is_multiline ( self . prev_token . span . until ( self . token . span ) ) {
1154- // The current token is in the same line as the prior token, not recoverable.
1155- } else if [ token:: Comma , token:: Colon ] . contains ( & self . token . kind )
1156- && self . prev_token . kind == token:: CloseDelim ( token:: Paren )
1157- {
1158- // Likely typo: The current token is on a new line and is expected to be
1159- // `.`, `;`, `?`, or an operator after a close delimiter token.
1160- //
1161- // let a = std::process::Command::new("echo")
1162- // .arg("1")
1163- // ,arg("2")
1164- // ^
1165- // https://github.com/rust-lang/rust/issues/72253
1166- self . expect ( & token:: Semi ) ?;
1167- return Ok ( ( ) ) ;
1168- } else if self . look_ahead ( 1 , |t| {
1169- t == & token:: CloseDelim ( token:: Brace ) || t. can_begin_expr ( ) && t. kind != token:: Colon
1170- } ) && [ token:: Comma , token:: Colon ] . contains ( & self . token . kind )
1171- {
1172- // Likely typo: `,` → `;` or `:` → `;`. This is triggered if the current token is
1173- // either `,` or `:`, and the next token could either start a new statement or is a
1174- // block close. For example:
1175- //
1176- // let x = 32:
1177- // let y = 42;
1178- self . bump ( ) ;
1179- let sp = self . prev_token . span ;
1180- self . struct_span_err ( sp, & msg)
1181- . span_suggestion_short ( sp, "change this to `;`" , ";" . to_string ( ) , appl)
1182- . emit ( ) ;
1183- return Ok ( ( ) ) ;
1184- } else if self . look_ahead ( 0 , |t| {
1185- t == & token:: CloseDelim ( token:: Brace )
1186- || (
1187- t. can_begin_expr ( ) && t != & token:: Semi && t != & token:: Pound
1188- // Avoid triggering with too many trailing `#` in raw string.
1189- )
1190- } ) {
1191- // Missing semicolon typo. This is triggered if the next token could either start a
1192- // new statement or is a block close. For example:
1193- //
1194- // let x = 32
1195- // let y = 42;
1196- let sp = self . prev_token . span . shrink_to_hi ( ) ;
1197- self . struct_span_err ( sp, & msg)
1198- . span_label ( self . token . span , "unexpected token" )
1199- . span_suggestion_short ( sp, "add `;` here" , ";" . to_string ( ) , appl)
1200- . emit ( ) ;
1201- return Ok ( ( ) ) ;
1202- }
12031203 self . expect ( & token:: Semi ) . map ( drop) // Error unconditionally
12041204 }
12051205
0 commit comments