]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_parse/src/parser/diagnostics.rs
New upstream version 1.71.1+dfsg1
[rustc.git] / compiler / rustc_parse / src / parser / diagnostics.rs
CommitLineData
a2a8927a 1use super::pat::Expected;
a2a8927a 2use super::{
923072b8
FG
3 BlockMode, CommaRecoveryMode, Parser, PathStyle, Restrictions, SemiColonMode, SeqSep,
4 TokenExpectType, TokenType,
a2a8927a 5};
2b03887a 6use crate::errors::{
49aad941 7 AmbiguousPlus, AttributeOnParamType, BadQPathStage2, BadTypePlus, BadTypePlusSub, ColonAsSemi,
2b03887a 8 ComparisonOperatorsCannotBeChained, ComparisonOperatorsCannotBeChainedSugg,
353b0b11
FG
9 ConstGenericWithoutBraces, ConstGenericWithoutBracesSugg, DocCommentDoesNotDocumentAnything,
10 DocCommentOnParamType, DoubleColonInBound, ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg,
11 GenericParamsWithoutAngleBrackets, GenericParamsWithoutAngleBracketsSugg,
12 HelpIdentifierStartsWithNumber, InInTypo, IncorrectAwait, IncorrectSemicolon,
13 IncorrectUseOfAwait, ParenthesesInForHead, ParenthesesInForHeadSugg,
14 PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst,
15 StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, StructLiteralNeedingParens,
49aad941 16 StructLiteralNeedingParensSugg, SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma,
353b0b11
FG
17 UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration,
18 UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead,
2b03887a 19};
60c5eb7d 20
9ffffee4 21use crate::fluent_generated as fluent;
487cf647 22use crate::parser;
5869c6ff 23use rustc_ast as ast;
74b04a01 24use rustc_ast::ptr::P;
04454e1e 25use rustc_ast::token::{self, Delimiter, Lit, LitKind, TokenKind};
74b04a01 26use rustc_ast::util::parser::AssocOp;
3c0e092e 27use rustc_ast::{
f2b60f7d
FG
28 AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingAnnotation, Block,
29 BlockCheckMode, Expr, ExprKind, GenericArg, Generics, Item, ItemKind, Param, Pat, PatKind,
30 Path, PathSegment, QSelf, Ty, TyKind,
3c0e092e 31};
74b04a01 32use rustc_ast_pretty::pprust;
dc9dc135 33use rustc_data_structures::fx::FxHashSet;
04454e1e 34use rustc_errors::{
49aad941
FG
35 pluralize, AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticMessage,
36 ErrorGuaranteed, FatalError, Handler, IntoDiagnostic, MultiSpan, PResult,
04454e1e 37};
2b03887a 38use rustc_session::errors::ExprParenthesesNeeded;
dfeec247 39use rustc_span::source_map::Spanned;
f2b60f7d 40use rustc_span::symbol::{kw, sym, Ident};
353b0b11 41use rustc_span::{Span, SpanSnippetError, Symbol, DUMMY_SP};
a2a8927a 42use std::mem::take;
487cf647
FG
43use std::ops::{Deref, DerefMut};
44use thin_vec::{thin_vec, ThinVec};
dc9dc135
XL
45
46/// Creates a placeholder argument.
e74abb32 47pub(super) fn dummy_arg(ident: Ident) -> Param {
dc9dc135
XL
48 let pat = P(Pat {
49 id: ast::DUMMY_NODE_ID,
f2b60f7d 50 kind: PatKind::Ident(BindingAnnotation::NONE, ident, None),
dc9dc135 51 span: ident.span,
3dfed10e 52 tokens: None,
dc9dc135 53 });
1b1a35ee 54 let ty = Ty { kind: TyKind::Err, span: ident.span, id: ast::DUMMY_NODE_ID, tokens: None };
e1599b0c 55 Param {
dfeec247 56 attrs: AttrVec::default(),
e1599b0c
XL
57 id: ast::DUMMY_NODE_ID,
58 pat,
59 span: ident.span,
60 ty: P(ty),
61 is_placeholder: false,
62 }
dc9dc135
XL
63}
64
e74abb32 65pub(super) trait RecoverQPath: Sized + 'static {
48663c56
XL
66 const PATH_STYLE: PathStyle = PathStyle::Expr;
67 fn to_ty(&self) -> Option<P<Ty>>;
487cf647 68 fn recovered(qself: Option<P<QSelf>>, path: ast::Path) -> Self;
48663c56
XL
69}
70
71impl RecoverQPath for Ty {
72 const PATH_STYLE: PathStyle = PathStyle::Type;
73 fn to_ty(&self) -> Option<P<Ty>> {
74 Some(P(self.clone()))
75 }
487cf647 76 fn recovered(qself: Option<P<QSelf>>, path: ast::Path) -> Self {
1b1a35ee
XL
77 Self {
78 span: path.span,
79 kind: TyKind::Path(qself, path),
80 id: ast::DUMMY_NODE_ID,
81 tokens: None,
82 }
48663c56
XL
83 }
84}
85
86impl RecoverQPath for Pat {
49aad941 87 const PATH_STYLE: PathStyle = PathStyle::Pat;
48663c56
XL
88 fn to_ty(&self) -> Option<P<Ty>> {
89 self.to_ty()
90 }
487cf647 91 fn recovered(qself: Option<P<QSelf>>, path: ast::Path) -> Self {
3dfed10e
XL
92 Self {
93 span: path.span,
94 kind: PatKind::Path(qself, path),
95 id: ast::DUMMY_NODE_ID,
96 tokens: None,
97 }
48663c56
XL
98 }
99}
100
101impl RecoverQPath for Expr {
102 fn to_ty(&self) -> Option<P<Ty>> {
103 self.to_ty()
104 }
487cf647 105 fn recovered(qself: Option<P<QSelf>>, path: ast::Path) -> Self {
48663c56
XL
106 Self {
107 span: path.span,
e74abb32 108 kind: ExprKind::Path(qself, path),
dfeec247 109 attrs: AttrVec::new(),
48663c56 110 id: ast::DUMMY_NODE_ID,
f9f354fc 111 tokens: None,
48663c56
XL
112 }
113 }
114}
115
e74abb32 116/// Control whether the closing delimiter should be consumed when calling `Parser::consume_block`.
923072b8 117pub(crate) enum ConsumeClosingDelim {
e74abb32
XL
118 Yes,
119 No,
120}
121
29967ef6
XL
122#[derive(Clone, Copy)]
123pub enum AttemptLocalParseRecovery {
124 Yes,
125 No,
126}
127
128impl AttemptLocalParseRecovery {
129 pub fn yes(&self) -> bool {
130 match self {
131 AttemptLocalParseRecovery::Yes => true,
132 AttemptLocalParseRecovery::No => false,
133 }
134 }
135
136 pub fn no(&self) -> bool {
137 match self {
138 AttemptLocalParseRecovery::Yes => false,
139 AttemptLocalParseRecovery::No => true,
140 }
141 }
142}
143
5e7ed085
FG
144/// Information for emitting suggestions and recovering from
145/// C-style `i++`, `--i`, etc.
146#[derive(Debug, Copy, Clone)]
147struct IncDecRecovery {
148 /// Is this increment/decrement its own statement?
149 standalone: IsStandalone,
150 /// Is this an increment or decrement?
151 op: IncOrDec,
152 /// Is this pre- or postfix?
153 fixity: UnaryFixity,
154}
155
156/// Is an increment or decrement expression its own statement?
157#[derive(Debug, Copy, Clone)]
158enum IsStandalone {
159 /// It's standalone, i.e., its own statement.
160 Standalone,
161 /// It's a subexpression, i.e., *not* standalone.
162 Subexpr,
5e7ed085
FG
163}
164
165#[derive(Debug, Copy, Clone, PartialEq, Eq)]
166enum IncOrDec {
167 Inc,
5e7ed085
FG
168 Dec,
169}
170
171#[derive(Debug, Copy, Clone, PartialEq, Eq)]
172enum UnaryFixity {
173 Pre,
174 Post,
175}
176
177impl IncOrDec {
178 fn chr(&self) -> char {
179 match self {
180 Self::Inc => '+',
181 Self::Dec => '-',
182 }
183 }
184
185 fn name(&self) -> &'static str {
186 match self {
187 Self::Inc => "increment",
188 Self::Dec => "decrement",
189 }
190 }
191}
192
193impl std::fmt::Display for UnaryFixity {
194 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
195 match self {
196 Self::Pre => write!(f, "prefix"),
197 Self::Post => write!(f, "postfix"),
198 }
199 }
200}
201
202struct MultiSugg {
203 msg: String,
204 patches: Vec<(Span, String)>,
205 applicability: Applicability,
206}
207
208impl MultiSugg {
f2b60f7d 209 fn emit(self, err: &mut Diagnostic) {
49aad941 210 err.multipart_suggestion(self.msg, self.patches, self.applicability);
5e7ed085
FG
211 }
212
9c376795 213 fn emit_verbose(self, err: &mut Diagnostic) {
49aad941 214 err.multipart_suggestion_verbose(self.msg, self.patches, self.applicability);
5e7ed085
FG
215 }
216}
04454e1e 217
487cf647
FG
218/// SnapshotParser is used to create a snapshot of the parser
219/// without causing duplicate errors being emitted when the `Parser`
220/// is dropped.
923072b8 221pub struct SnapshotParser<'a> {
5e7ed085 222 parser: Parser<'a>,
5e7ed085
FG
223}
224
225impl<'a> Deref for SnapshotParser<'a> {
226 type Target = Parser<'a>;
227
228 fn deref(&self) -> &Self::Target {
229 &self.parser
230 }
231}
232
233impl<'a> DerefMut for SnapshotParser<'a> {
234 fn deref_mut(&mut self) -> &mut Self::Target {
235 &mut self.parser
236 }
237}
238
48663c56 239impl<'a> Parser<'a> {
064997fb 240 #[rustc_lint_diagnostics]
49aad941 241 #[track_caller]
5e7ed085
FG
242 pub fn struct_span_err<S: Into<MultiSpan>>(
243 &self,
244 sp: S,
04454e1e 245 m: impl Into<DiagnosticMessage>,
5e7ed085 246 ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
dc9dc135
XL
247 self.sess.span_diagnostic.struct_span_err(sp, m)
248 }
249
04454e1e 250 pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, m: impl Into<DiagnosticMessage>) -> ! {
dc9dc135
XL
251 self.sess.span_diagnostic.span_bug(sp, m)
252 }
253
60c5eb7d 254 pub(super) fn diagnostic(&self) -> &'a Handler {
dc9dc135
XL
255 &self.sess.span_diagnostic
256 }
257
353b0b11 258 /// Replace `self` with `snapshot.parser`.
5e7ed085
FG
259 pub(super) fn restore_snapshot(&mut self, snapshot: SnapshotParser<'a>) {
260 *self = snapshot.parser;
5e7ed085
FG
261 }
262
263 /// Create a snapshot of the `Parser`.
923072b8 264 pub fn create_snapshot_for_diagnostic(&self) -> SnapshotParser<'a> {
353b0b11
FG
265 let snapshot = self.clone();
266 SnapshotParser { parser: snapshot }
5e7ed085
FG
267 }
268
e74abb32 269 pub(super) fn span_to_snippet(&self, span: Span) -> Result<String, SpanSnippetError> {
416331ca
XL
270 self.sess.source_map().span_to_snippet(span)
271 }
272
353b0b11
FG
273 /// Emits an error with suggestions if an identifier was expected but not found.
274 ///
275 /// Returns a possibly recovered identifier.
276 pub(super) fn expected_ident_found(
277 &mut self,
278 recover: bool,
279 ) -> PResult<'a, (Ident, /* is_raw */ bool)> {
280 if let TokenKind::DocComment(..) = self.prev_token.kind {
281 return Err(DocCommentDoesNotDocumentAnything {
282 span: self.prev_token.span,
283 missing_comma: None,
284 }
285 .into_diagnostic(&self.sess.span_diagnostic));
286 }
287
60c5eb7d
XL
288 let valid_follow = &[
289 TokenKind::Eq,
290 TokenKind::Colon,
291 TokenKind::Comma,
292 TokenKind::Semi,
293 TokenKind::ModSep,
04454e1e
FG
294 TokenKind::OpenDelim(Delimiter::Brace),
295 TokenKind::OpenDelim(Delimiter::Parenthesis),
296 TokenKind::CloseDelim(Delimiter::Brace),
297 TokenKind::CloseDelim(Delimiter::Parenthesis),
60c5eb7d 298 ];
353b0b11
FG
299
300 let mut recovered_ident = None;
301 // we take this here so that the correct original token is retained in
302 // the diagnostic, regardless of eager recovery.
303 let bad_token = self.token.clone();
304
305 // suggest prepending a keyword in identifier position with `r#`
306 let suggest_raw = if let Some((ident, false)) = self.token.ident()
307 && ident.is_raw_guess()
308 && self.look_ahead(1, |t| valid_follow.contains(&t.kind))
309 {
310 recovered_ident = Some((ident, true));
311
312 // `Symbol::to_string()` is different from `Symbol::into_diagnostic_arg()`,
313 // which uses `Symbol::to_ident_string()` and "helpfully" adds an implicit `r#`
314 let ident_name = ident.name.to_string();
315
316 Some(SuggEscapeIdentifier {
317 span: ident.span.shrink_to_lo(),
318 ident_name
319 })
320 } else { None };
2b03887a
FG
321
322 let suggest_remove_comma =
dc9dc135 323 if self.token == token::Comma && self.look_ahead(1, |t| t.is_ident()) {
353b0b11
FG
324 if recover {
325 self.bump();
326 recovered_ident = self.ident_or_err(false).ok();
327 };
328
329 Some(SuggRemoveComma { span: bad_token.span })
2b03887a
FG
330 } else {
331 None
332 };
333
353b0b11
FG
334 let help_cannot_start_number = self.is_lit_bad_ident().map(|(len, valid_portion)| {
335 let (invalid, valid) = self.token.span.split_at(len as u32);
336
337 recovered_ident = Some((Ident::new(valid_portion, valid), false));
338
339 HelpIdentifierStartsWithNumber { num_span: invalid }
340 });
341
2b03887a 342 let err = ExpectedIdentifier {
353b0b11
FG
343 span: bad_token.span,
344 token: bad_token,
2b03887a
FG
345 suggest_raw,
346 suggest_remove_comma,
353b0b11 347 help_cannot_start_number,
2b03887a 348 };
9ffffee4
FG
349 let mut err = err.into_diagnostic(&self.sess.span_diagnostic);
350
351 // if the token we have is a `<`
352 // it *might* be a misplaced generic
353b0b11 353 // FIXME: could we recover with this?
9ffffee4
FG
354 if self.token == token::Lt {
355 // all keywords that could have generic applied
356 let valid_prev_keywords =
357 [kw::Fn, kw::Type, kw::Struct, kw::Enum, kw::Union, kw::Trait];
358
359 // If we've expected an identifier,
360 // and the current token is a '<'
361 // if the previous token is a valid keyword
362 // that might use a generic, then suggest a correct
363 // generic placement (later on)
364 let maybe_keyword = self.prev_token.clone();
365 if valid_prev_keywords.into_iter().any(|x| maybe_keyword.is_keyword(x)) {
366 // if we have a valid keyword, attempt to parse generics
367 // also obtain the keywords symbol
368 match self.parse_generics() {
369 Ok(generic) => {
370 if let TokenKind::Ident(symbol, _) = maybe_keyword.kind {
371 let ident_name = symbol;
372 // at this point, we've found something like
373 // `fn <T>id`
374 // and current token should be Ident with the item name (i.e. the function name)
375 // if there is a `<` after the fn name, then don't show a suggestion, show help
376
377 if !self.look_ahead(1, |t| *t == token::Lt) &&
378 let Ok(snippet) = self.sess.source_map().span_to_snippet(generic.span) {
379 err.multipart_suggestion_verbose(
380 format!("place the generic parameter name after the {ident_name} name"),
381 vec![
382 (self.token.span.shrink_to_hi(), snippet),
383 (generic.span, String::new())
384 ],
385 Applicability::MaybeIncorrect,
386 );
387 } else {
388 err.help(format!(
389 "place the generic parameter name after the {ident_name} name"
390 ));
391 }
392 }
393 }
394 Err(err) => {
395 // if there's an error parsing the generics,
396 // then don't do a misplaced generics suggestion
397 // and emit the expected ident error instead;
398 err.cancel();
399 }
400 }
401 }
402 }
403
353b0b11
FG
404 if let Some(recovered_ident) = recovered_ident && recover {
405 err.emit();
406 Ok(recovered_ident)
407 } else {
408 Err(err)
409 }
410 }
411
412 pub(super) fn expected_ident_found_err(&mut self) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
413 self.expected_ident_found(false).unwrap_err()
414 }
415
416 /// Checks if the current token is a integer or float literal and looks like
417 /// it could be a invalid identifier with digits at the start.
418 ///
419 /// Returns the number of characters (bytes) composing the invalid portion
420 /// of the identifier and the valid portion of the identifier.
421 pub(super) fn is_lit_bad_ident(&mut self) -> Option<(usize, Symbol)> {
422 // ensure that the integer literal is followed by a *invalid*
423 // suffix: this is how we know that it is a identifier with an
424 // invalid beginning.
425 if let token::Literal(Lit {
426 kind: token::LitKind::Integer | token::LitKind::Float,
427 symbol,
428 suffix: Some(suffix), // no suffix makes it a valid literal
429 }) = self.token.kind
430 && rustc_ast::MetaItemLit::from_token(&self.token).is_none()
431 {
432 Some((symbol.as_str().len(), suffix))
433 } else {
434 None
435 }
dc9dc135
XL
436 }
437
e74abb32 438 pub(super) fn expected_one_of_not_found(
dc9dc135
XL
439 &mut self,
440 edible: &[TokenKind],
441 inedible: &[TokenKind],
442 ) -> PResult<'a, bool /* recovered */> {
5869c6ff 443 debug!("expected_one_of_not_found(edible: {:?}, inedible: {:?})", edible, inedible);
dc9dc135
XL
444 fn tokens_to_string(tokens: &[TokenType]) -> String {
445 let mut i = tokens.iter();
e1599b0c 446 // This might be a sign we need a connect method on `Iterator`.
6a06907d 447 let b = i.next().map_or_else(String::new, |t| t.to_string());
dc9dc135
XL
448 i.enumerate().fold(b, |mut b, (i, a)| {
449 if tokens.len() > 2 && i == tokens.len() - 2 {
450 b.push_str(", or ");
451 } else if tokens.len() == 2 && i == tokens.len() - 2 {
452 b.push_str(" or ");
453 } else {
454 b.push_str(", ");
455 }
456 b.push_str(&a.to_string());
457 b
458 })
459 }
460
dfeec247
XL
461 let mut expected = edible
462 .iter()
dc9dc135
XL
463 .map(|x| TokenType::Token(x.clone()))
464 .chain(inedible.iter().map(|x| TokenType::Token(x.clone())))
465 .chain(self.expected_tokens.iter().cloned())
923072b8
FG
466 .filter_map(|token| {
467 // filter out suggestions which suggest the same token which was found and deemed incorrect
468 fn is_ident_eq_keyword(found: &TokenKind, expected: &TokenType) -> bool {
469 if let TokenKind::Ident(current_sym, _) = found {
470 if let TokenType::Keyword(suggested_sym) = expected {
471 return current_sym == suggested_sym;
472 }
473 }
474 false
475 }
476 if token != parser::TokenType::Token(self.token.kind.clone()) {
477 let eq = is_ident_eq_keyword(&self.token.kind, &token);
478 // if the suggestion is a keyword and the found token is an ident,
479 // the content of which are equal to the suggestion's content,
480 // we can remove that suggestion (see the return None statement below)
481
482 // if this isn't the case however, and the suggestion is a token the
483 // content of which is the same as the found token's, we remove it as well
484 if !eq {
485 if let TokenType::Token(kind) = &token {
486 if kind == &self.token.kind {
487 return None;
488 }
489 }
490 return Some(token);
491 }
492 }
493 return None;
494 })
dc9dc135
XL
495 .collect::<Vec<_>>();
496 expected.sort_by_cached_key(|x| x.to_string());
497 expected.dedup();
5869c6ff 498
94222f64 499 let sm = self.sess.source_map();
2b03887a
FG
500
501 // Special-case "expected `;`" errors
94222f64
XL
502 if expected.contains(&TokenType::Token(token::Semi)) {
503 if self.token.span == DUMMY_SP || self.prev_token.span == DUMMY_SP {
504 // Likely inside a macro, can't provide meaningful suggestions.
505 } else if !sm.is_multiline(self.prev_token.span.until(self.token.span)) {
506 // The current token is in the same line as the prior token, not recoverable.
507 } else if [token::Comma, token::Colon].contains(&self.token.kind)
04454e1e 508 && self.prev_token.kind == token::CloseDelim(Delimiter::Parenthesis)
94222f64
XL
509 {
510 // Likely typo: The current token is on a new line and is expected to be
511 // `.`, `;`, `?`, or an operator after a close delimiter token.
512 //
513 // let a = std::process::Command::new("echo")
514 // .arg("1")
515 // ,arg("2")
516 // ^
517 // https://github.com/rust-lang/rust/issues/72253
518 } else if self.look_ahead(1, |t| {
04454e1e 519 t == &token::CloseDelim(Delimiter::Brace)
94222f64
XL
520 || t.can_begin_expr() && t.kind != token::Colon
521 }) && [token::Comma, token::Colon].contains(&self.token.kind)
522 {
523 // Likely typo: `,` → `;` or `:` → `;`. This is triggered if the current token is
524 // either `,` or `:`, and the next token could either start a new statement or is a
525 // block close. For example:
526 //
527 // let x = 32:
528 // let y = 42;
2b03887a
FG
529 self.sess.emit_err(ExpectedSemi {
530 span: self.token.span,
531 token: self.token.clone(),
532 unexpected_token_label: None,
533 sugg: ExpectedSemiSugg::ChangeToSemi(self.token.span),
534 });
94222f64 535 self.bump();
c295e0f8 536 return Ok(true);
94222f64 537 } else if self.look_ahead(0, |t| {
04454e1e 538 t == &token::CloseDelim(Delimiter::Brace)
f2b60f7d
FG
539 || ((t.can_begin_expr() || t.can_begin_item())
540 && t != &token::Semi
541 && t != &token::Pound)
04454e1e
FG
542 // Avoid triggering with too many trailing `#` in raw string.
543 || (sm.is_multiline(
f2b60f7d 544 self.prev_token.span.shrink_to_hi().until(self.token.span.shrink_to_lo()),
04454e1e 545 ) && t == &token::Pound)
064997fb
FG
546 }) && !expected.contains(&TokenType::Token(token::Comma))
547 {
94222f64
XL
548 // Missing semicolon typo. This is triggered if the next token could either start a
549 // new statement or is a block close. For example:
550 //
551 // let x = 32
552 // let y = 42;
2b03887a
FG
553 let span = self.prev_token.span.shrink_to_hi();
554 self.sess.emit_err(ExpectedSemi {
555 span,
556 token: self.token.clone(),
557 unexpected_token_label: Some(self.token.span),
558 sugg: ExpectedSemiSugg::AddSemi(span),
559 });
c295e0f8 560 return Ok(true);
94222f64
XL
561 }
562 }
563
f2b60f7d
FG
564 if self.token.kind == TokenKind::EqEq
565 && self.prev_token.is_ident()
566 && expected.iter().any(|tok| matches!(tok, TokenType::Token(TokenKind::Eq)))
567 {
568 // Likely typo: `=` → `==` in let expr or enum item
569 return Err(self.sess.create_err(UseEqInstead { span: self.token.span }));
570 }
571
a2a8927a 572 let expect = tokens_to_string(&expected);
dfeec247 573 let actual = super::token_descr(&self.token);
dc9dc135 574 let (msg_exp, (label_sp, label_exp)) = if expected.len() > 1 {
49aad941 575 let fmt = format!("expected one of {expect}, found {actual}");
dc9dc135
XL
576 let short_expect = if expected.len() > 6 {
577 format!("{} possible tokens", expected.len())
578 } else {
49aad941 579 expect
dc9dc135 580 };
49aad941 581 (fmt, (self.prev_token.span.shrink_to_hi(), format!("expected one of {short_expect}")))
dc9dc135 582 } else if expected.is_empty() {
dfeec247 583 (
f2b60f7d 584 format!("unexpected token: {actual}"),
74b04a01 585 (self.prev_token.span, "unexpected token after this".to_string()),
dfeec247 586 )
dc9dc135 587 } else {
dfeec247 588 (
5e7ed085
FG
589 format!("expected {expect}, found {actual}"),
590 (self.prev_token.span.shrink_to_hi(), format!("expected {expect}")),
dfeec247 591 )
dc9dc135
XL
592 };
593 self.last_unexpected_token_span = Some(self.token.span);
2b03887a 594 // FIXME: translation requires list formatting (for `expect`)
49aad941 595 let mut err = self.struct_span_err(self.token.span, msg_exp);
5869c6ff 596
064997fb 597 if let TokenKind::Ident(symbol, _) = &self.prev_token.kind {
f2b60f7d 598 if ["def", "fun", "func", "function"].contains(&symbol.as_str()) {
064997fb
FG
599 err.span_suggestion_short(
600 self.prev_token.span,
49aad941 601 format!("write `fn` instead of `{symbol}` to declare a function"),
f2b60f7d 602 "fn",
2b03887a 603 Applicability::MachineApplicable,
064997fb
FG
604 );
605 }
606 }
607
f2b60f7d
FG
608 // `pub` may be used for an item or `pub(crate)`
609 if self.prev_token.is_ident_named(sym::public)
610 && (self.token.can_begin_item()
611 || self.token.kind == TokenKind::OpenDelim(Delimiter::Parenthesis))
612 {
613 err.span_suggestion_short(
614 self.prev_token.span,
615 "write `pub` instead of `public` to make the item public",
616 "pub",
2b03887a 617 Applicability::MachineApplicable,
f2b60f7d
FG
618 );
619 }
620
5869c6ff
XL
621 // Add suggestion for a missing closing angle bracket if '>' is included in expected_tokens
622 // there are unclosed angle brackets
623 if self.unmatched_angle_bracket_count > 0
624 && self.token.kind == TokenKind::Eq
625 && expected.iter().any(|tok| matches!(tok, TokenType::Token(TokenKind::Gt)))
626 {
627 err.span_label(self.prev_token.span, "maybe try to close unmatched angle bracket");
628 }
629
dc9dc135 630 let sp = if self.token == token::Eof {
e1599b0c 631 // This is EOF; don't want to point at the following char, but rather the last token.
74b04a01 632 self.prev_token.span
dc9dc135
XL
633 } else {
634 label_sp
635 };
dc9dc135 636
ba9703b0 637 if self.check_too_many_raw_str_terminators(&mut err) {
04454e1e
FG
638 if expected.contains(&TokenType::Token(token::Semi)) && self.eat(&token::Semi) {
639 err.emit();
640 return Ok(true);
641 } else {
642 return Err(err);
643 }
ba9703b0
XL
644 }
645
74b04a01 646 if self.prev_token.span == DUMMY_SP {
e74abb32
XL
647 // Account for macro context where the previous span might not be
648 // available to avoid incorrect output (#54841).
649 err.span_label(self.token.span, label_exp);
650 } else if !sm.is_multiline(self.token.span.shrink_to_hi().until(sp.shrink_to_lo())) {
651 // When the spans are in the same line, it means that the only content between
652 // them is whitespace, point at the found token in that case:
653 //
654 // X | () => { syntax error };
655 // | ^^^^^ expected one of 8 possible tokens here
656 //
657 // instead of having:
658 //
659 // X | () => { syntax error };
660 // | -^^^^^ unexpected token
661 // | |
662 // | expected one of 8 possible tokens here
663 err.span_label(self.token.span, label_exp);
664 } else {
665 err.span_label(sp, label_exp);
666 err.span_label(self.token.span, "unexpected token");
dc9dc135
XL
667 }
668 Err(err)
669 }
670
5e7ed085 671 fn check_too_many_raw_str_terminators(&mut self, err: &mut Diagnostic) -> bool {
04454e1e 672 let sm = self.sess.source_map();
ba9703b0
XL
673 match (&self.prev_token.kind, &self.token.kind) {
674 (
675 TokenKind::Literal(Lit {
676 kind: LitKind::StrRaw(n_hashes) | LitKind::ByteStrRaw(n_hashes),
677 ..
678 }),
679 TokenKind::Pound,
04454e1e
FG
680 ) if !sm.is_multiline(
681 self.prev_token.span.shrink_to_hi().until(self.token.span.shrink_to_lo()),
682 ) =>
683 {
684 let n_hashes: u8 = *n_hashes;
ba9703b0 685 err.set_primary_message("too many `#` when terminating raw string");
04454e1e
FG
686 let str_span = self.prev_token.span;
687 let mut span = self.token.span;
688 let mut count = 0;
689 while self.token.kind == TokenKind::Pound
690 && !sm.is_multiline(span.shrink_to_hi().until(self.token.span.shrink_to_lo()))
691 {
692 span = span.with_hi(self.token.span.hi());
693 self.bump();
694 count += 1;
695 }
696 err.set_span(span);
ba9703b0 697 err.span_suggestion(
04454e1e 698 span,
49aad941 699 format!("remove the extra `#`{}", pluralize!(count)),
923072b8 700 "",
ba9703b0
XL
701 Applicability::MachineApplicable,
702 );
04454e1e
FG
703 err.span_label(
704 str_span,
49aad941 705 format!("this raw string started with {n_hashes} `#`{}", pluralize!(n_hashes)),
04454e1e 706 );
ba9703b0
XL
707 true
708 }
709 _ => false,
710 }
711 }
712
29967ef6
XL
713 pub fn maybe_suggest_struct_literal(
714 &mut self,
715 lo: Span,
716 s: BlockCheckMode,
9c376795
FG
717 maybe_struct_name: token::Token,
718 can_be_struct_literal: bool,
29967ef6
XL
719 ) -> Option<PResult<'a, P<Block>>> {
720 if self.token.is_ident() && self.look_ahead(1, |t| t == &token::Colon) {
721 // We might be having a struct literal where people forgot to include the path:
722 // fn foo() -> Foo {
723 // field: value,
724 // }
9c376795 725 info!(?maybe_struct_name, ?self.token);
5e7ed085 726 let mut snapshot = self.create_snapshot_for_diagnostic();
487cf647
FG
727 let path = Path {
728 segments: ThinVec::new(),
729 span: self.prev_token.span.shrink_to_lo(),
730 tokens: None,
731 };
9ffffee4 732 let struct_expr = snapshot.parse_expr_struct(None, path, false);
29967ef6
XL
733 let block_tail = self.parse_block_tail(lo, s, AttemptLocalParseRecovery::No);
734 return Some(match (struct_expr, block_tail) {
735 (Ok(expr), Err(mut err)) => {
736 // We have encountered the following:
737 // fn foo() -> Foo {
738 // field: value,
739 // }
740 // Suggest:
741 // fn foo() -> Foo { Path {
742 // field: value,
743 // } }
744 err.delay_as_bug();
5e7ed085 745 self.restore_snapshot(snapshot);
94222f64 746 let mut tail = self.mk_block(
9ffffee4 747 thin_vec![self.mk_stmt_err(expr.span)],
29967ef6
XL
748 s,
749 lo.to(self.prev_token.span),
94222f64
XL
750 );
751 tail.could_be_bare_literal = true;
9c376795
FG
752 if maybe_struct_name.is_ident() && can_be_struct_literal {
753 // Account for `if Example { a: one(), }.is_pos() {}`.
754 Err(self.sess.create_err(StructLiteralNeedingParens {
755 span: maybe_struct_name.span.to(expr.span),
756 sugg: StructLiteralNeedingParensSugg {
757 before: maybe_struct_name.span.shrink_to_lo(),
758 after: expr.span.shrink_to_hi(),
759 },
760 }))
761 } else {
762 self.sess.emit_err(StructLiteralBodyWithoutPath {
763 span: expr.span,
764 sugg: StructLiteralBodyWithoutPathSugg {
765 before: expr.span.shrink_to_lo(),
766 after: expr.span.shrink_to_hi(),
767 },
768 });
769 Ok(tail)
770 }
29967ef6 771 }
5e7ed085 772 (Err(err), Ok(tail)) => {
29967ef6
XL
773 // We have a block tail that contains a somehow valid type ascription expr.
774 err.cancel();
775 Ok(tail)
776 }
5e7ed085 777 (Err(snapshot_err), Err(err)) => {
29967ef6
XL
778 // We don't know what went wrong, emit the normal error.
779 snapshot_err.cancel();
04454e1e 780 self.consume_block(Delimiter::Brace, ConsumeClosingDelim::Yes);
29967ef6
XL
781 Err(err)
782 }
94222f64
XL
783 (Ok(_), Ok(mut tail)) => {
784 tail.could_be_bare_literal = true;
785 Ok(tail)
786 }
29967ef6
XL
787 });
788 }
789 None
790 }
791
dc9dc135
XL
792 /// Eats and discards tokens until one of `kets` is encountered. Respects token trees,
793 /// passes through any errors encountered. Used for error recovery.
e74abb32 794 pub(super) fn eat_to_tokens(&mut self, kets: &[&TokenKind]) {
5e7ed085 795 if let Err(err) =
dfeec247
XL
796 self.parse_seq_to_before_tokens(kets, SeqSep::none(), TokenExpectType::Expect, |p| {
797 Ok(p.parse_token_tree())
798 })
799 {
e1599b0c 800 err.cancel();
dc9dc135
XL
801 }
802 }
803
804 /// This function checks if there are trailing angle brackets and produces
805 /// a diagnostic to suggest removing them.
806 ///
807 /// ```ignore (diagnostic)
5099ac24
FG
808 /// let _ = [1, 2, 3].into_iter().collect::<Vec<usize>>>>();
809 /// ^^ help: remove extra angle brackets
dc9dc135 810 /// ```
f035d41b
XL
811 ///
812 /// If `true` is returned, then trailing brackets were recovered, tokens were consumed
813 /// up until one of the tokens in 'end' was encountered, and an error was emitted.
814 pub(super) fn check_trailing_angle_brackets(
815 &mut self,
816 segment: &PathSegment,
817 end: &[&TokenKind],
818 ) -> bool {
487cf647
FG
819 if !self.may_recover() {
820 return false;
821 }
822
dc9dc135
XL
823 // This function is intended to be invoked after parsing a path segment where there are two
824 // cases:
825 //
826 // 1. A specific token is expected after the path segment.
827 // eg. `x.foo(`, `x.foo::<u32>(` (parenthesis - method call),
828 // `Foo::`, or `Foo::<Bar>::` (mod sep - continued path).
829 // 2. No specific token is expected after the path segment.
830 // eg. `x.foo` (field access)
831 //
832 // This function is called after parsing `.foo` and before parsing the token `end` (if
833 // present). This includes any angle bracket arguments, such as `.foo::<u32>` or
834 // `Foo::<Bar>`.
835
836 // We only care about trailing angle brackets if we previously parsed angle bracket
837 // arguments. This helps stop us incorrectly suggesting that extra angle brackets be
838 // removed in this case:
839 //
840 // `x.foo >> (3)` (where `x.foo` is a `u32` for example)
841 //
842 // This case is particularly tricky as we won't notice it just looking at the tokens -
843 // it will appear the same (in terms of upcoming tokens) as below (since the `::<u32>` will
844 // have already been parsed):
845 //
846 // `x.foo::<u32>>>(3)`
dfeec247 847 let parsed_angle_bracket_args =
49aad941 848 segment.args.as_ref().is_some_and(|args| args.is_angle_bracketed());
dc9dc135
XL
849
850 debug!(
851 "check_trailing_angle_brackets: parsed_angle_bracket_args={:?}",
852 parsed_angle_bracket_args,
853 );
854 if !parsed_angle_bracket_args {
f035d41b 855 return false;
dc9dc135
XL
856 }
857
858 // Keep the span at the start so we can highlight the sequence of `>` characters to be
859 // removed.
860 let lo = self.token.span;
861
862 // We need to look-ahead to see if we have `>` characters without moving the cursor forward
863 // (since we might have the field access case and the characters we're eating are
864 // actual operators and not trailing characters - ie `x.foo >> 3`).
865 let mut position = 0;
866
867 // We can encounter `>` or `>>` tokens in any order, so we need to keep track of how
868 // many of each (so we can correctly pluralize our error messages) and continue to
869 // advance.
870 let mut number_of_shr = 0;
871 let mut number_of_gt = 0;
872 while self.look_ahead(position, |t| {
873 trace!("check_trailing_angle_brackets: t={:?}", t);
874 if *t == token::BinOp(token::BinOpToken::Shr) {
875 number_of_shr += 1;
876 true
877 } else if *t == token::Gt {
878 number_of_gt += 1;
879 true
880 } else {
881 false
882 }
883 }) {
884 position += 1;
885 }
886
887 // If we didn't find any trailing `>` characters, then we have nothing to error about.
888 debug!(
889 "check_trailing_angle_brackets: number_of_gt={:?} number_of_shr={:?}",
890 number_of_gt, number_of_shr,
891 );
892 if number_of_gt < 1 && number_of_shr < 1 {
f035d41b 893 return false;
dc9dc135
XL
894 }
895
896 // Finally, double check that we have our end token as otherwise this is the
897 // second case.
898 if self.look_ahead(position, |t| {
899 trace!("check_trailing_angle_brackets: t={:?}", t);
f035d41b 900 end.contains(&&t.kind)
dc9dc135
XL
901 }) {
902 // Eat from where we started until the end token so that parsing can continue
903 // as if we didn't have those extra angle brackets.
f035d41b 904 self.eat_to_tokens(end);
dc9dc135
XL
905 let span = lo.until(self.token.span);
906
2b03887a
FG
907 let num_extra_brackets = number_of_gt + number_of_shr * 2;
908 self.sess.emit_err(UnmatchedAngleBrackets { span, num_extra_brackets });
f035d41b 909 return true;
dfeec247 910 }
f035d41b 911 false
dfeec247
XL
912 }
913
3dfed10e
XL
914 /// Check if a method call with an intended turbofish has been written without surrounding
915 /// angle brackets.
916 pub(super) fn check_turbofish_missing_angle_brackets(&mut self, segment: &mut PathSegment) {
487cf647
FG
917 if !self.may_recover() {
918 return;
919 }
920
3dfed10e 921 if token::ModSep == self.token.kind && segment.args.is_none() {
5e7ed085 922 let snapshot = self.create_snapshot_for_diagnostic();
3dfed10e
XL
923 self.bump();
924 let lo = self.token.span;
3c0e092e 925 match self.parse_angle_args(None) {
3dfed10e
XL
926 Ok(args) => {
927 let span = lo.to(self.prev_token.span);
928 // Detect trailing `>` like in `x.collect::Vec<_>>()`.
929 let mut trailing_span = self.prev_token.span.shrink_to_hi();
930 while self.token.kind == token::BinOp(token::Shr)
931 || self.token.kind == token::Gt
932 {
933 trailing_span = trailing_span.to(self.token.span);
934 self.bump();
935 }
04454e1e 936 if self.token.kind == token::OpenDelim(Delimiter::Parenthesis) {
3dfed10e 937 // Recover from bad turbofish: `foo.collect::Vec<_>()`.
353b0b11 938 segment.args = Some(AngleBracketedArgs { args, span }.into());
3dfed10e 939
2b03887a 940 self.sess.emit_err(GenericParamsWithoutAngleBrackets {
3dfed10e 941 span,
2b03887a
FG
942 sugg: GenericParamsWithoutAngleBracketsSugg {
943 left: span.shrink_to_lo(),
944 right: trailing_span,
945 },
946 });
3dfed10e
XL
947 } else {
948 // This doesn't look like an invalid turbofish, can't recover parse state.
5e7ed085 949 self.restore_snapshot(snapshot);
3dfed10e
XL
950 }
951 }
5e7ed085 952 Err(err) => {
6a06907d 953 // We couldn't parse generic parameters, unlikely to be a turbofish. Rely on
3dfed10e
XL
954 // generic parse error instead.
955 err.cancel();
5e7ed085 956 self.restore_snapshot(snapshot);
3dfed10e
XL
957 }
958 }
959 }
960 }
961
1b1a35ee
XL
962 /// When writing a turbofish with multiple type parameters missing the leading `::`, we will
963 /// encounter a parse error when encountering the first `,`.
964 pub(super) fn check_mistyped_turbofish_with_multiple_type_params(
965 &mut self,
5e7ed085 966 mut e: DiagnosticBuilder<'a, ErrorGuaranteed>,
1b1a35ee
XL
967 expr: &mut P<Expr>,
968 ) -> PResult<'a, ()> {
5e7ed085
FG
969 if let ExprKind::Binary(binop, _, _) = &expr.kind
970 && let ast::BinOpKind::Lt = binop.node
971 && self.eat(&token::Comma)
972 {
973 let x = self.parse_seq_to_before_end(
974 &token::Gt,
975 SeqSep::trailing_allowed(token::Comma),
976 |p| p.parse_generic_arg(None),
977 );
978 match x {
979 Ok((_, _, false)) => {
980 if self.eat(&token::Gt) {
981 e.span_suggestion_verbose(
982 binop.span.shrink_to_lo(),
487cf647 983 fluent::parse_sugg_turbofish_syntax,
923072b8 984 "::",
5e7ed085
FG
985 Applicability::MaybeIncorrect,
986 )
987 .emit();
988 match self.parse_expr() {
989 Ok(_) => {
990 *expr =
991 self.mk_expr_err(expr.span.to(self.prev_token.span));
992 return Ok(());
993 }
994 Err(err) => {
995 *expr = self.mk_expr_err(expr.span);
996 err.cancel();
1b1a35ee
XL
997 }
998 }
1b1a35ee
XL
999 }
1000 }
5e7ed085
FG
1001 Err(err) => {
1002 err.cancel();
1003 }
1004 _ => {}
1b1a35ee
XL
1005 }
1006 }
1007 Err(e)
1008 }
1009
49aad941
FG
1010 /// Suggest add the missing `let` before the identifier in stmt
1011 /// `a: Ty = 1` -> `let a: Ty = 1`
1012 pub(super) fn suggest_add_missing_let_for_stmt(
1013 &mut self,
1014 err: &mut DiagnosticBuilder<'a, ErrorGuaranteed>,
1015 ) {
1016 if self.token == token::Colon {
1017 let prev_span = self.prev_token.span.shrink_to_lo();
1018 let snapshot = self.create_snapshot_for_diagnostic();
1019 self.bump();
1020 match self.parse_ty() {
1021 Ok(_) => {
1022 if self.token == token::Eq {
1023 let sugg = SuggAddMissingLetStmt { span: prev_span };
1024 sugg.add_to_diagnostic(err);
1025 }
1026 }
1027 Err(e) => {
1028 e.cancel();
1029 }
1030 }
1031 self.restore_snapshot(snapshot);
1032 }
1033 }
1034
dfeec247
XL
1035 /// Check to see if a pair of chained operators looks like an attempt at chained comparison,
1036 /// e.g. `1 < x <= 3`. If so, suggest either splitting the comparison into two, or
1037 /// parenthesising the leftmost comparison.
1038 fn attempt_chained_comparison_suggestion(
1039 &mut self,
2b03887a 1040 err: &mut ComparisonOperatorsCannotBeChained,
dfeec247
XL
1041 inner_op: &Expr,
1042 outer_op: &Spanned<AssocOp>,
ba9703b0 1043 ) -> bool /* advanced the cursor */ {
487cf647 1044 if let ExprKind::Binary(op, l1, r1) = &inner_op.kind {
5e7ed085
FG
1045 if let ExprKind::Field(_, ident) = l1.kind
1046 && ident.as_str().parse::<i32>().is_err()
1047 && !matches!(r1.kind, ExprKind::Lit(_))
1048 {
1049 // The parser has encountered `foo.bar<baz`, the likelihood of the turbofish
1050 // suggestion being the only one to apply is high.
1051 return false;
ba9703b0 1052 }
ba9703b0
XL
1053 return match (op.node, &outer_op.node) {
1054 // `x == y == z`
1055 (BinOpKind::Eq, AssocOp::Equal) |
dfeec247 1056 // `x < y < z` and friends.
ba9703b0
XL
1057 (BinOpKind::Lt, AssocOp::Less | AssocOp::LessEqual) |
1058 (BinOpKind::Le, AssocOp::LessEqual | AssocOp::Less) |
dfeec247 1059 // `x > y > z` and friends.
ba9703b0
XL
1060 (BinOpKind::Gt, AssocOp::Greater | AssocOp::GreaterEqual) |
1061 (BinOpKind::Ge, AssocOp::GreaterEqual | AssocOp::Greater) => {
dfeec247
XL
1062 let expr_to_str = |e: &Expr| {
1063 self.span_to_snippet(e.span)
1064 .unwrap_or_else(|_| pprust::expr_to_string(&e))
1065 };
2b03887a
FG
1066 err.chaining_sugg = Some(ComparisonOperatorsCannotBeChainedSugg::SplitComparison {
1067 span: inner_op.span.shrink_to_hi(),
1068 middle_term: expr_to_str(&r1),
1069 });
ba9703b0 1070 false // Keep the current parse behavior, where the AST is `(x < y) < z`.
dfeec247 1071 }
ba9703b0
XL
1072 // `x == y < z`
1073 (BinOpKind::Eq, AssocOp::Less | AssocOp::LessEqual | AssocOp::Greater | AssocOp::GreaterEqual) => {
1074 // Consume `z`/outer-op-rhs.
5e7ed085 1075 let snapshot = self.create_snapshot_for_diagnostic();
ba9703b0
XL
1076 match self.parse_expr() {
1077 Ok(r2) => {
1078 // We are sure that outer-op-rhs could be consumed, the suggestion is
1079 // likely correct.
2b03887a
FG
1080 err.chaining_sugg = Some(ComparisonOperatorsCannotBeChainedSugg::Parenthesize {
1081 left: r1.span.shrink_to_lo(),
1082 right: r2.span.shrink_to_hi(),
1083 });
ba9703b0
XL
1084 true
1085 }
5e7ed085 1086 Err(expr_err) => {
ba9703b0 1087 expr_err.cancel();
5e7ed085 1088 self.restore_snapshot(snapshot);
ba9703b0
XL
1089 false
1090 }
1091 }
1092 }
1093 // `x > y == z`
1094 (BinOpKind::Lt | BinOpKind::Le | BinOpKind::Gt | BinOpKind::Ge, AssocOp::Equal) => {
5e7ed085 1095 let snapshot = self.create_snapshot_for_diagnostic();
ba9703b0
XL
1096 // At this point it is always valid to enclose the lhs in parentheses, no
1097 // further checks are necessary.
1098 match self.parse_expr() {
1099 Ok(_) => {
2b03887a
FG
1100 err.chaining_sugg = Some(ComparisonOperatorsCannotBeChainedSugg::Parenthesize {
1101 left: l1.span.shrink_to_lo(),
1102 right: r1.span.shrink_to_hi(),
1103 });
ba9703b0
XL
1104 true
1105 }
5e7ed085 1106 Err(expr_err) => {
ba9703b0 1107 expr_err.cancel();
5e7ed085 1108 self.restore_snapshot(snapshot);
ba9703b0
XL
1109 false
1110 }
1111 }
1112 }
1113 _ => false,
1114 };
dc9dc135 1115 }
ba9703b0 1116 false
dc9dc135
XL
1117 }
1118
e1599b0c 1119 /// Produces an error if comparison operators are chained (RFC #558).
e74abb32
XL
1120 /// We only need to check the LHS, not the RHS, because all comparison ops have same
1121 /// precedence (see `fn precedence`) and are left-associative (see `fn fixity`).
1122 ///
1123 /// This can also be hit if someone incorrectly writes `foo<bar>()` when they should have used
1124 /// the turbofish (`foo::<bar>()`) syntax. We attempt some heuristic recovery if that is the
1125 /// case.
1126 ///
1127 /// Keep in mind that given that `outer_op.is_comparison()` holds and comparison ops are left
1128 /// associative we can infer that we have:
1129 ///
ba9703b0 1130 /// ```text
e74abb32
XL
1131 /// outer_op
1132 /// / \
1133 /// inner_op r2
1134 /// / \
dfeec247 1135 /// l1 r1
ba9703b0 1136 /// ```
e74abb32
XL
1137 pub(super) fn check_no_chained_comparison(
1138 &mut self,
dfeec247
XL
1139 inner_op: &Expr,
1140 outer_op: &Spanned<AssocOp>,
e74abb32
XL
1141 ) -> PResult<'a, Option<P<Expr>>> {
1142 debug_assert!(
dfeec247 1143 outer_op.node.is_comparison(),
e74abb32 1144 "check_no_chained_comparison: {:?} is not comparison",
dfeec247 1145 outer_op.node,
e74abb32
XL
1146 );
1147
f2b60f7d 1148 let mk_err_expr = |this: &Self, span| Ok(Some(this.mk_expr(span, ExprKind::Err)));
e74abb32 1149
487cf647
FG
1150 match &inner_op.kind {
1151 ExprKind::Binary(op, l1, r1) if op.node.is_comparison() => {
2b03887a
FG
1152 let mut err = ComparisonOperatorsCannotBeChained {
1153 span: vec![op.span, self.prev_token.span],
1154 suggest_turbofish: None,
1155 help_turbofish: None,
1156 chaining_sugg: None,
e74abb32
XL
1157 };
1158
ba9703b0
XL
1159 // Include `<` to provide this recommendation even in a case like
1160 // `Foo<Bar<Baz<Qux, ()>>>`
1161 if op.node == BinOpKind::Lt && outer_op.node == AssocOp::Less
1162 || outer_op.node == AssocOp::Greater
dfeec247 1163 {
dfeec247 1164 if outer_op.node == AssocOp::Less {
5e7ed085 1165 let snapshot = self.create_snapshot_for_diagnostic();
e74abb32
XL
1166 self.bump();
1167 // So far we have parsed `foo<bar<`, consume the rest of the type args.
dfeec247
XL
1168 let modifiers =
1169 [(token::Lt, 1), (token::Gt, -1), (token::BinOp(token::Shr), -2)];
a2a8927a 1170 self.consume_tts(1, &modifiers);
e74abb32 1171
04454e1e 1172 if !&[token::OpenDelim(Delimiter::Parenthesis), token::ModSep]
dfeec247
XL
1173 .contains(&self.token.kind)
1174 {
e74abb32
XL
1175 // We don't have `foo< bar >(` or `foo< bar >::`, so we rewind the
1176 // parser and bail out.
5e7ed085 1177 self.restore_snapshot(snapshot);
e74abb32
XL
1178 }
1179 }
1180 return if token::ModSep == self.token.kind {
1181 // We have some certainty that this was a bad turbofish at this point.
1182 // `foo< bar >::`
9c376795
FG
1183 if let ExprKind::Binary(o, ..) = inner_op.kind && o.node == BinOpKind::Lt {
1184 err.suggest_turbofish = Some(op.span.shrink_to_lo());
1185 } else {
1186 err.help_turbofish = Some(());
1187 }
e74abb32 1188
5e7ed085 1189 let snapshot = self.create_snapshot_for_diagnostic();
e74abb32
XL
1190 self.bump(); // `::`
1191
1192 // Consume the rest of the likely `foo<bar>::new()` or return at `foo<bar>`.
1193 match self.parse_expr() {
1194 Ok(_) => {
1195 // 99% certain that the suggestion is correct, continue parsing.
2b03887a 1196 self.sess.emit_err(err);
e74abb32
XL
1197 // FIXME: actually check that the two expressions in the binop are
1198 // paths and resynthesize new fn call expression instead of using
1199 // `ExprKind::Err` placeholder.
74b04a01 1200 mk_err_expr(self, inner_op.span.to(self.prev_token.span))
e74abb32 1201 }
5e7ed085 1202 Err(expr_err) => {
e74abb32
XL
1203 expr_err.cancel();
1204 // Not entirely sure now, but we bubble the error up with the
1205 // suggestion.
5e7ed085 1206 self.restore_snapshot(snapshot);
2b03887a 1207 Err(err.into_diagnostic(&self.sess.span_diagnostic))
e74abb32
XL
1208 }
1209 }
04454e1e 1210 } else if token::OpenDelim(Delimiter::Parenthesis) == self.token.kind {
e74abb32
XL
1211 // We have high certainty that this was a bad turbofish at this point.
1212 // `foo< bar >(`
9c376795
FG
1213 if let ExprKind::Binary(o, ..) = inner_op.kind && o.node == BinOpKind::Lt {
1214 err.suggest_turbofish = Some(op.span.shrink_to_lo());
1215 } else {
1216 err.help_turbofish = Some(());
1217 }
e74abb32
XL
1218 // Consume the fn call arguments.
1219 match self.consume_fn_args() {
2b03887a 1220 Err(()) => Err(err.into_diagnostic(&self.sess.span_diagnostic)),
e74abb32 1221 Ok(()) => {
2b03887a 1222 self.sess.emit_err(err);
e74abb32
XL
1223 // FIXME: actually check that the two expressions in the binop are
1224 // paths and resynthesize new fn call expression instead of using
1225 // `ExprKind::Err` placeholder.
74b04a01 1226 mk_err_expr(self, inner_op.span.to(self.prev_token.span))
e74abb32
XL
1227 }
1228 }
1229 } else {
ba9703b0
XL
1230 if !matches!(l1.kind, ExprKind::Lit(_))
1231 && !matches!(r1.kind, ExprKind::Lit(_))
1232 {
1233 // All we know is that this is `foo < bar >` and *nothing* else. Try to
1234 // be helpful, but don't attempt to recover.
2b03887a 1235 err.help_turbofish = Some(());
ba9703b0
XL
1236 }
1237
1238 // If it looks like a genuine attempt to chain operators (as opposed to a
1239 // misformatted turbofish, for instance), suggest a correct form.
1240 if self.attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op)
1241 {
2b03887a 1242 self.sess.emit_err(err);
ba9703b0
XL
1243 mk_err_expr(self, inner_op.span.to(self.prev_token.span))
1244 } else {
1245 // These cases cause too many knock-down errors, bail out (#61329).
2b03887a 1246 Err(err.into_diagnostic(&self.sess.span_diagnostic))
ba9703b0 1247 }
e74abb32 1248 };
dc9dc135 1249 }
ba9703b0
XL
1250 let recover =
1251 self.attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op);
2b03887a 1252 self.sess.emit_err(err);
ba9703b0
XL
1253 if recover {
1254 return mk_err_expr(self, inner_op.span.to(self.prev_token.span));
1255 }
dc9dc135
XL
1256 }
1257 _ => {}
1258 }
e74abb32
XL
1259 Ok(None)
1260 }
1261
1262 fn consume_fn_args(&mut self) -> Result<(), ()> {
5e7ed085 1263 let snapshot = self.create_snapshot_for_diagnostic();
e74abb32
XL
1264 self.bump(); // `(`
1265
1266 // Consume the fn call arguments.
04454e1e
FG
1267 let modifiers = [
1268 (token::OpenDelim(Delimiter::Parenthesis), 1),
1269 (token::CloseDelim(Delimiter::Parenthesis), -1),
1270 ];
a2a8927a 1271 self.consume_tts(1, &modifiers);
e74abb32
XL
1272
1273 if self.token.kind == token::Eof {
1274 // Not entirely sure that what we consumed were fn arguments, rollback.
5e7ed085 1275 self.restore_snapshot(snapshot);
e74abb32
XL
1276 Err(())
1277 } else {
1278 // 99% certain that the suggestion is correct, continue parsing.
1279 Ok(())
1280 }
dc9dc135
XL
1281 }
1282
923072b8
FG
1283 pub(super) fn maybe_report_ambiguous_plus(&mut self, impl_dyn_multi: bool, ty: &Ty) {
1284 if impl_dyn_multi {
04454e1e 1285 self.sess.emit_err(AmbiguousPlus { sum_ty: pprust::ty_to_string(&ty), span: ty.span });
48663c56
XL
1286 }
1287 }
1288
5099ac24 1289 /// Swift lets users write `Ty?` to mean `Option<Ty>`. Parse the construct and recover from it.
923072b8 1290 pub(super) fn maybe_recover_from_question_mark(&mut self, ty: P<Ty>) -> P<Ty> {
5099ac24
FG
1291 if self.token == token::Question {
1292 self.bump();
2b03887a
FG
1293 self.sess.emit_err(QuestionMarkInType {
1294 span: self.prev_token.span,
1295 sugg: QuestionMarkInTypeSugg {
1296 left: ty.span.shrink_to_lo(),
1297 right: self.prev_token.span,
1298 },
1299 });
5099ac24
FG
1300 self.mk_ty(ty.span.to(self.prev_token.span), TyKind::Err)
1301 } else {
1302 ty
1303 }
1304 }
1305
923072b8 1306 pub(super) fn maybe_recover_from_bad_type_plus(&mut self, ty: &Ty) -> PResult<'a, ()> {
48663c56 1307 // Do not add `+` to expected tokens.
923072b8 1308 if !self.token.is_like_plus() {
48663c56
XL
1309 return Ok(());
1310 }
1311
1312 self.bump(); // `+`
49aad941 1313 let bounds = self.parse_generic_bounds()?;
74b04a01 1314 let sum_span = ty.span.to(self.prev_token.span);
48663c56 1315
487cf647 1316 let sub = match &ty.kind {
9c376795 1317 TyKind::Ref(lifetime, mut_ty) => {
48663c56 1318 let sum_with_parens = pprust::to_string(|s| {
416331ca
XL
1319 s.s.word("&");
1320 s.print_opt_lifetime(lifetime);
60c5eb7d 1321 s.print_mutability(mut_ty.mutbl, false);
416331ca
XL
1322 s.popen();
1323 s.print_type(&mut_ty.ty);
923072b8
FG
1324 if !bounds.is_empty() {
1325 s.word(" + ");
1326 s.print_type_bounds(&bounds);
1327 }
48663c56
XL
1328 s.pclose()
1329 });
923072b8
FG
1330
1331 BadTypePlusSub::AddParen { sum_with_parens, span: sum_span }
48663c56 1332 }
923072b8
FG
1333 TyKind::Ptr(..) | TyKind::BareFn(..) => BadTypePlusSub::ForgotParen { span: sum_span },
1334 _ => BadTypePlusSub::ExpectPath { span: sum_span },
1335 };
1336
1337 self.sess.emit_err(BadTypePlus { ty: pprust::ty_to_string(ty), span: sum_span, sub });
1338
48663c56
XL
1339 Ok(())
1340 }
1341
5e7ed085
FG
1342 pub(super) fn recover_from_prefix_increment(
1343 &mut self,
1344 operand_expr: P<Expr>,
1345 op_span: Span,
9c376795 1346 start_stmt: bool,
5e7ed085 1347 ) -> PResult<'a, P<Expr>> {
9c376795 1348 let standalone = if start_stmt { IsStandalone::Standalone } else { IsStandalone::Subexpr };
5e7ed085 1349 let kind = IncDecRecovery { standalone, op: IncOrDec::Inc, fixity: UnaryFixity::Pre };
5e7ed085
FG
1350 self.recover_from_inc_dec(operand_expr, kind, op_span)
1351 }
1352
1353 pub(super) fn recover_from_postfix_increment(
1354 &mut self,
1355 operand_expr: P<Expr>,
1356 op_span: Span,
9c376795 1357 start_stmt: bool,
5e7ed085
FG
1358 ) -> PResult<'a, P<Expr>> {
1359 let kind = IncDecRecovery {
9c376795 1360 standalone: if start_stmt { IsStandalone::Standalone } else { IsStandalone::Subexpr },
5e7ed085
FG
1361 op: IncOrDec::Inc,
1362 fixity: UnaryFixity::Post,
1363 };
5e7ed085
FG
1364 self.recover_from_inc_dec(operand_expr, kind, op_span)
1365 }
1366
9ffffee4
FG
1367 pub(super) fn recover_from_postfix_decrement(
1368 &mut self,
1369 operand_expr: P<Expr>,
1370 op_span: Span,
1371 start_stmt: bool,
1372 ) -> PResult<'a, P<Expr>> {
1373 let kind = IncDecRecovery {
1374 standalone: if start_stmt { IsStandalone::Standalone } else { IsStandalone::Subexpr },
1375 op: IncOrDec::Dec,
1376 fixity: UnaryFixity::Post,
1377 };
1378 self.recover_from_inc_dec(operand_expr, kind, op_span)
1379 }
1380
5e7ed085
FG
1381 fn recover_from_inc_dec(
1382 &mut self,
1383 base: P<Expr>,
1384 kind: IncDecRecovery,
1385 op_span: Span,
1386 ) -> PResult<'a, P<Expr>> {
1387 let mut err = self.struct_span_err(
1388 op_span,
49aad941 1389 format!("Rust has no {} {} operator", kind.fixity, kind.op.name()),
5e7ed085 1390 );
49aad941 1391 err.span_label(op_span, format!("not a valid {} operator", kind.fixity));
5e7ed085
FG
1392
1393 let help_base_case = |mut err: DiagnosticBuilder<'_, _>, base| {
49aad941 1394 err.help(format!("use `{}= 1` instead", kind.op.chr()));
5e7ed085
FG
1395 err.emit();
1396 Ok(base)
1397 };
1398
1399 // (pre, post)
1400 let spans = match kind.fixity {
1401 UnaryFixity::Pre => (op_span, base.span.shrink_to_hi()),
1402 UnaryFixity::Post => (base.span.shrink_to_lo(), op_span),
1403 };
1404
1405 match kind.standalone {
9c376795
FG
1406 IsStandalone::Standalone => {
1407 self.inc_dec_standalone_suggest(kind, spans).emit_verbose(&mut err)
1408 }
5e7ed085
FG
1409 IsStandalone::Subexpr => {
1410 let Ok(base_src) = self.span_to_snippet(base.span)
9c376795 1411 else { return help_base_case(err, base) };
5e7ed085
FG
1412 match kind.fixity {
1413 UnaryFixity::Pre => {
1414 self.prefix_inc_dec_suggest(base_src, kind, spans).emit(&mut err)
1415 }
1416 UnaryFixity::Post => {
9c376795
FG
1417 // won't suggest since we can not handle the precedences
1418 // for example: `a + b++` has been parsed (a + b)++ and we can not suggest here
1419 if !matches!(base.kind, ExprKind::Binary(_, _, _)) {
1420 self.postfix_inc_dec_suggest(base_src, kind, spans).emit(&mut err)
1421 }
5e7ed085
FG
1422 }
1423 }
1424 }
5e7ed085
FG
1425 }
1426 Err(err)
1427 }
1428
1429 fn prefix_inc_dec_suggest(
1430 &mut self,
1431 base_src: String,
1432 kind: IncDecRecovery,
1433 (pre_span, post_span): (Span, Span),
1434 ) -> MultiSugg {
1435 MultiSugg {
1436 msg: format!("use `{}= 1` instead", kind.op.chr()),
1437 patches: vec![
1438 (pre_span, "{ ".to_string()),
1439 (post_span, format!(" {}= 1; {} }}", kind.op.chr(), base_src)),
1440 ],
1441 applicability: Applicability::MachineApplicable,
1442 }
1443 }
1444
1445 fn postfix_inc_dec_suggest(
1446 &mut self,
1447 base_src: String,
1448 kind: IncDecRecovery,
1449 (pre_span, post_span): (Span, Span),
1450 ) -> MultiSugg {
1451 let tmp_var = if base_src.trim() == "tmp" { "tmp_" } else { "tmp" };
1452 MultiSugg {
1453 msg: format!("use `{}= 1` instead", kind.op.chr()),
1454 patches: vec![
f2b60f7d 1455 (pre_span, format!("{{ let {tmp_var} = ")),
5e7ed085
FG
1456 (post_span, format!("; {} {}= 1; {} }}", base_src, kind.op.chr(), tmp_var)),
1457 ],
1458 applicability: Applicability::HasPlaceholders,
1459 }
1460 }
1461
1462 fn inc_dec_standalone_suggest(
1463 &mut self,
1464 kind: IncDecRecovery,
1465 (pre_span, post_span): (Span, Span),
1466 ) -> MultiSugg {
2b03887a
FG
1467 let mut patches = Vec::new();
1468
1469 if !pre_span.is_empty() {
1470 patches.push((pre_span, String::new()));
1471 }
1472
1473 patches.push((post_span, format!(" {}= 1", kind.op.chr())));
5e7ed085
FG
1474 MultiSugg {
1475 msg: format!("use `{}= 1` instead", kind.op.chr()),
2b03887a 1476 patches,
5e7ed085
FG
1477 applicability: Applicability::MachineApplicable,
1478 }
1479 }
1480
e1599b0c
XL
1481 /// Tries to recover from associated item paths like `[T]::AssocItem` / `(T, U)::AssocItem`.
1482 /// Attempts to convert the base expression/pattern/type into a type, parses the `::AssocItem`
1483 /// tail, and combines them into a `<Ty>::AssocItem` expression/pattern/type.
e74abb32 1484 pub(super) fn maybe_recover_from_bad_qpath<T: RecoverQPath>(
48663c56
XL
1485 &mut self,
1486 base: P<T>,
48663c56 1487 ) -> PResult<'a, P<T>> {
487cf647
FG
1488 if !self.may_recover() {
1489 return Ok(base);
1490 }
1491
48663c56 1492 // Do not add `::` to expected tokens.
923072b8 1493 if self.token == token::ModSep {
48663c56
XL
1494 if let Some(ty) = base.to_ty() {
1495 return self.maybe_recover_from_bad_qpath_stage_2(ty.span, ty);
1496 }
1497 }
1498 Ok(base)
1499 }
1500
e1599b0c
XL
1501 /// Given an already parsed `Ty`, parses the `::AssocItem` tail and
1502 /// combines them into a `<Ty>::AssocItem` expression/pattern/type.
e74abb32 1503 pub(super) fn maybe_recover_from_bad_qpath_stage_2<T: RecoverQPath>(
48663c56
XL
1504 &mut self,
1505 ty_span: Span,
1506 ty: P<Ty>,
1507 ) -> PResult<'a, P<T>> {
1508 self.expect(&token::ModSep)?;
1509
487cf647 1510 let mut path = ast::Path { segments: ThinVec::new(), span: DUMMY_SP, tokens: None };
3c0e092e 1511 self.parse_path_segments(&mut path.segments, T::PATH_STYLE, None)?;
74b04a01 1512 path.span = ty_span.to(self.prev_token.span);
48663c56 1513
dfeec247 1514 let ty_str = self.span_to_snippet(ty_span).unwrap_or_else(|_| pprust::ty_to_string(&ty));
923072b8
FG
1515 self.sess.emit_err(BadQPathStage2 {
1516 span: path.span,
1517 ty: format!("<{}>::{}", ty_str, pprust::path_to_string(&path)),
1518 });
48663c56 1519
e1599b0c 1520 let path_span = ty_span.shrink_to_hi(); // Use an empty path since `position == 0`.
487cf647 1521 Ok(P(T::recovered(Some(P(QSelf { ty, path_span, position: 0 })), path)))
48663c56
XL
1522 }
1523
3c0e092e 1524 pub fn maybe_consume_incorrect_semicolon(&mut self, items: &[P<Item>]) -> bool {
a2a8927a
XL
1525 if self.token.kind == TokenKind::Semi {
1526 self.bump();
923072b8
FG
1527
1528 let mut err =
1529 IncorrectSemicolon { span: self.prev_token.span, opt_help: None, name: "" };
1530
48663c56
XL
1531 if !items.is_empty() {
1532 let previous_item = &items[items.len() - 1];
e74abb32 1533 let previous_item_kind_name = match previous_item.kind {
e1599b0c
XL
1534 // Say "braced struct" because tuple-structs and
1535 // braceless-empty-struct declarations do take a semicolon.
48663c56
XL
1536 ItemKind::Struct(..) => Some("braced struct"),
1537 ItemKind::Enum(..) => Some("enum"),
1538 ItemKind::Trait(..) => Some("trait"),
1539 ItemKind::Union(..) => Some("union"),
1540 _ => None,
1541 };
1542 if let Some(name) = previous_item_kind_name {
923072b8
FG
1543 err.opt_help = Some(());
1544 err.name = name;
48663c56
XL
1545 }
1546 }
923072b8 1547 self.sess.emit_err(err);
48663c56
XL
1548 true
1549 } else {
1550 false
1551 }
1552 }
1553
e1599b0c 1554 /// Creates a `DiagnosticBuilder` for an unexpected token `t` and tries to recover if it is a
dc9dc135 1555 /// closing delimiter.
e74abb32 1556 pub(super) fn unexpected_try_recover(
dc9dc135
XL
1557 &mut self,
1558 t: &TokenKind,
1559 ) -> PResult<'a, bool /* recovered */> {
1560 let token_str = pprust::token_kind_to_string(t);
dfeec247 1561 let this_token_str = super::token_descr(&self.token);
dc9dc135
XL
1562 let (prev_sp, sp) = match (&self.token.kind, self.subparser_name) {
1563 // Point at the end of the macro call when reaching end of macro arguments.
1564 (token::Eof, Some(_)) => {
2b03887a 1565 let sp = self.prev_token.span.shrink_to_hi();
dc9dc135
XL
1566 (sp, sp)
1567 }
1568 // We don't want to point at the following span after DUMMY_SP.
1569 // This happens when the parser finds an empty TokenStream.
74b04a01 1570 _ if self.prev_token.span == DUMMY_SP => (self.token.span, self.token.span),
dc9dc135 1571 // EOF, don't want to point at the following char, but rather the last token.
74b04a01
XL
1572 (token::Eof, None) => (self.prev_token.span, self.token.span),
1573 _ => (self.prev_token.span.shrink_to_hi(), self.token.span),
dc9dc135
XL
1574 };
1575 let msg = format!(
1576 "expected `{}`, found {}",
1577 token_str,
1578 match (&self.token.kind, self.subparser_name) {
5e7ed085 1579 (token::Eof, Some(origin)) => format!("end of {origin}"),
dc9dc135
XL
1580 _ => this_token_str,
1581 },
1582 );
49aad941 1583 let mut err = self.struct_span_err(sp, msg);
5e7ed085 1584 let label_exp = format!("expected `{token_str}`");
416331ca 1585 let sm = self.sess.source_map();
e74abb32
XL
1586 if !sm.is_multiline(prev_sp.until(sp)) {
1587 // When the spans are in the same line, it means that the only content
1588 // between them is whitespace, point only at the found token.
1589 err.span_label(sp, label_exp);
1590 } else {
1591 err.span_label(prev_sp, label_exp);
1592 err.span_label(sp, "unexpected token");
dc9dc135
XL
1593 }
1594 Err(err)
1595 }
1596
e74abb32 1597 pub(super) fn expect_semi(&mut self) -> PResult<'a, ()> {
49aad941 1598 if self.eat(&token::Semi) || self.recover_colon_as_semi() {
e74abb32
XL
1599 return Ok(());
1600 }
dfeec247 1601 self.expect(&token::Semi).map(drop) // Error unconditionally
e74abb32
XL
1602 }
1603
49aad941
FG
1604 pub(super) fn recover_colon_as_semi(&mut self) -> bool {
1605 let line_idx = |span: Span| {
1606 self.sess
1607 .source_map()
1608 .span_to_lines(span)
1609 .ok()
1610 .and_then(|lines| Some(lines.lines.get(0)?.line_index))
1611 };
1612
1613 if self.may_recover()
1614 && self.token == token::Colon
1615 && self.look_ahead(1, |next| line_idx(self.token.span) < line_idx(next.span))
1616 {
1617 self.sess.emit_err(ColonAsSemi {
1618 span: self.token.span,
1619 type_ascription: self.sess.unstable_features.is_nightly_build().then_some(()),
1620 });
1621 self.bump();
1622 return true;
1623 }
1624
1625 false
1626 }
1627
e1599b0c 1628 /// Consumes alternative await syntaxes like `await!(<expr>)`, `await <expr>`,
416331ca 1629 /// `await? <expr>`, `await(<expr>)`, and `await { <expr> }`.
dfeec247 1630 pub(super) fn recover_incorrect_await_syntax(
48663c56
XL
1631 &mut self,
1632 lo: Span,
1633 await_sp: Span,
dfeec247
XL
1634 ) -> PResult<'a, P<Expr>> {
1635 let (hi, expr, is_question) = if self.token == token::Not {
416331ca 1636 // Handle `await!(<expr>)`.
dfeec247
XL
1637 self.recover_await_macro()?
1638 } else {
1639 self.recover_await_prefix(await_sp)?
1640 };
1641 let sp = self.error_on_incorrect_await(lo, hi, &expr, is_question);
29967ef6
XL
1642 let kind = match expr.kind {
1643 // Avoid knock-down errors as we don't know whether to interpret this as `foo().await?`
1644 // or `foo()?.await` (the very reason we went with postfix syntax 😅).
1645 ExprKind::Try(_) => ExprKind::Err,
49aad941 1646 _ => ExprKind::Await(expr, await_sp),
29967ef6 1647 };
f2b60f7d 1648 let expr = self.mk_expr(lo.to(sp), kind);
923072b8 1649 self.maybe_recover_from_bad_qpath(expr)
dfeec247
XL
1650 }
1651
1652 fn recover_await_macro(&mut self) -> PResult<'a, (Span, P<Expr>, bool)> {
1653 self.expect(&token::Not)?;
04454e1e 1654 self.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
dfeec247 1655 let expr = self.parse_expr()?;
04454e1e 1656 self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
74b04a01 1657 Ok((self.prev_token.span, expr, false))
dfeec247 1658 }
416331ca 1659
dfeec247 1660 fn recover_await_prefix(&mut self, await_sp: Span) -> PResult<'a, (Span, P<Expr>, bool)> {
48663c56 1661 let is_question = self.eat(&token::Question); // Handle `await? <expr>`.
04454e1e 1662 let expr = if self.token == token::OpenDelim(Delimiter::Brace) {
48663c56 1663 // Handle `await { <expr> }`.
6a06907d 1664 // This needs to be handled separately from the next arm to avoid
48663c56 1665 // interpreting `await { <expr> }?` as `<expr>?.await`.
9ffffee4 1666 self.parse_expr_block(None, self.token.span, BlockCheckMode::Default)
48663c56
XL
1667 } else {
1668 self.parse_expr()
dfeec247
XL
1669 }
1670 .map_err(|mut err| {
48663c56
XL
1671 err.span_label(await_sp, "while parsing this incorrect await expression");
1672 err
1673 })?;
dfeec247 1674 Ok((expr.span, expr, is_question))
416331ca
XL
1675 }
1676
1677 fn error_on_incorrect_await(&self, lo: Span, hi: Span, expr: &Expr, is_question: bool) -> Span {
923072b8
FG
1678 let span = lo.to(hi);
1679 let applicability = match expr.kind {
48663c56
XL
1680 ExprKind::Try(_) => Applicability::MaybeIncorrect, // `await <expr>?`
1681 _ => Applicability::MachineApplicable,
1682 };
923072b8
FG
1683
1684 self.sess.emit_err(IncorrectAwait {
1685 span,
1686 sugg_span: (span, applicability),
1687 expr: self.span_to_snippet(expr.span).unwrap_or_else(|_| pprust::expr_to_string(&expr)),
1688 question_mark: if is_question { "?" } else { "" },
1689 });
1690
1691 span
48663c56
XL
1692 }
1693
e1599b0c 1694 /// If encountering `future.await()`, consumes and emits an error.
e74abb32 1695 pub(super) fn recover_from_await_method_call(&mut self) {
04454e1e
FG
1696 if self.token == token::OpenDelim(Delimiter::Parenthesis)
1697 && self.look_ahead(1, |t| t == &token::CloseDelim(Delimiter::Parenthesis))
48663c56
XL
1698 {
1699 // future.await()
dc9dc135 1700 let lo = self.token.span;
48663c56 1701 self.bump(); // (
923072b8 1702 let span = lo.to(self.token.span);
48663c56 1703 self.bump(); // )
923072b8
FG
1704
1705 self.sess.emit_err(IncorrectUseOfAwait { span });
48663c56
XL
1706 }
1707 }
1708
ba9703b0
XL
1709 pub(super) fn try_macro_suggestion(&mut self) -> PResult<'a, P<Expr>> {
1710 let is_try = self.token.is_keyword(kw::Try);
1711 let is_questionmark = self.look_ahead(1, |t| t == &token::Not); //check for !
04454e1e 1712 let is_open = self.look_ahead(2, |t| t == &token::OpenDelim(Delimiter::Parenthesis)); //check for (
ba9703b0
XL
1713
1714 if is_try && is_questionmark && is_open {
1715 let lo = self.token.span;
1716 self.bump(); //remove try
1717 self.bump(); //remove !
1718 let try_span = lo.to(self.token.span); //we take the try!( span
1719 self.bump(); //remove (
04454e1e
FG
1720 let is_empty = self.token == token::CloseDelim(Delimiter::Parenthesis); //check if the block is empty
1721 self.consume_block(Delimiter::Parenthesis, ConsumeClosingDelim::No); //eat the block
ba9703b0
XL
1722 let hi = self.token.span;
1723 self.bump(); //remove )
1724 let mut err = self.struct_span_err(lo.to(hi), "use of deprecated `try` macro");
1725 err.note("in the 2018 edition `try` is a reserved keyword, and the `try!()` macro is deprecated");
1726 let prefix = if is_empty { "" } else { "alternatively, " };
1727 if !is_empty {
1728 err.multipart_suggestion(
1729 "you can use the `?` operator instead",
1730 vec![(try_span, "".to_owned()), (hi, "?".to_owned())],
1731 Applicability::MachineApplicable,
1732 );
1733 }
49aad941 1734 err.span_suggestion(lo.shrink_to_lo(), format!("{prefix}you can still access the deprecated `try!()` macro using the \"raw identifier\" syntax"), "r#", Applicability::MachineApplicable);
ba9703b0
XL
1735 err.emit();
1736 Ok(self.mk_expr_err(lo.to(hi)))
1737 } else {
1738 Err(self.expected_expression_found()) // The user isn't trying to invoke the try! macro
1739 }
1740 }
1741
e1599b0c 1742 /// Recovers a situation like `for ( $pat in $expr )`
416331ca
XL
1743 /// and suggest writing `for $pat in $expr` instead.
1744 ///
1745 /// This should be called before parsing the `$block`.
e74abb32 1746 pub(super) fn recover_parens_around_for_head(
416331ca
XL
1747 &mut self,
1748 pat: P<Pat>,
416331ca
XL
1749 begin_paren: Option<Span>,
1750 ) -> P<Pat> {
1751 match (&self.token.kind, begin_paren) {
04454e1e 1752 (token::CloseDelim(Delimiter::Parenthesis), Some(begin_par_sp)) => {
416331ca
XL
1753 self.bump();
1754
487cf647
FG
1755 let sm = self.sess.source_map();
1756 let left = begin_par_sp;
1757 let right = self.prev_token.span;
1758 let left_snippet = if let Ok(snip) = sm.span_to_prev_source(left) &&
1759 !snip.ends_with(' ') {
1760 " ".to_string()
1761 } else {
1762 "".to_string()
1763 };
1764
1765 let right_snippet = if let Ok(snip) = sm.span_to_next_source(right) &&
1766 !snip.starts_with(' ') {
1767 " ".to_string()
1768 } else {
1769 "".to_string()
1770 };
1771
2b03887a 1772 self.sess.emit_err(ParenthesesInForHead {
487cf647 1773 span: vec![left, right],
c295e0f8
XL
1774 // With e.g. `for (x) in y)` this would replace `(x) in y)`
1775 // with `x) in y)` which is syntactically invalid.
1776 // However, this is prevented before we get here.
487cf647 1777 sugg: ParenthesesInForHeadSugg { left, right, left_snippet, right_snippet },
2b03887a 1778 });
416331ca
XL
1779
1780 // Unwrap `(pat)` into `pat` to avoid the `unused_parens` lint.
e74abb32 1781 pat.and_then(|pat| match pat.kind {
416331ca
XL
1782 PatKind::Paren(pat) => pat,
1783 _ => P(pat),
1784 })
1785 }
1786 _ => pat,
1787 }
1788 }
1789
e74abb32 1790 pub(super) fn recover_seq_parse_error(
48663c56 1791 &mut self,
04454e1e 1792 delim: Delimiter,
48663c56
XL
1793 lo: Span,
1794 result: PResult<'a, P<Expr>>,
1795 ) -> P<Expr> {
1796 match result {
1797 Ok(x) => x,
1798 Err(mut err) => {
1799 err.emit();
e74abb32
XL
1800 // Recover from parse error, callers expect the closing delim to be consumed.
1801 self.consume_block(delim, ConsumeClosingDelim::Yes);
f2b60f7d 1802 self.mk_expr(lo.to(self.prev_token.span), ExprKind::Err)
48663c56
XL
1803 }
1804 }
1805 }
1806
e1599b0c
XL
1807 /// Eats tokens until we can be relatively sure we reached the end of the
1808 /// statement. This is something of a best-effort heuristic.
1809 ///
1810 /// We terminate when we find an unmatched `}` (without consuming it).
e74abb32 1811 pub(super) fn recover_stmt(&mut self) {
48663c56
XL
1812 self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore)
1813 }
1814
e1599b0c
XL
1815 /// If `break_on_semi` is `Break`, then we will stop consuming tokens after
1816 /// finding (and consuming) a `;` outside of `{}` or `[]` (note that this is
1817 /// approximate -- it can mean we break too early due to macros, but that
1818 /// should only lead to sub-optimal recovery, not inaccurate parsing).
1819 ///
1820 /// If `break_on_block` is `Break`, then we will stop consuming tokens
1821 /// after finding (and consuming) a brace-delimited block.
e74abb32
XL
1822 pub(super) fn recover_stmt_(
1823 &mut self,
1824 break_on_semi: SemiColonMode,
1825 break_on_block: BlockMode,
1826 ) {
48663c56
XL
1827 let mut brace_depth = 0;
1828 let mut bracket_depth = 0;
1829 let mut in_block = false;
dfeec247 1830 debug!("recover_stmt_ enter loop (semi={:?}, block={:?})", break_on_semi, break_on_block);
48663c56
XL
1831 loop {
1832 debug!("recover_stmt_ loop {:?}", self.token);
dc9dc135 1833 match self.token.kind {
04454e1e 1834 token::OpenDelim(Delimiter::Brace) => {
48663c56
XL
1835 brace_depth += 1;
1836 self.bump();
dfeec247
XL
1837 if break_on_block == BlockMode::Break && brace_depth == 1 && bracket_depth == 0
1838 {
48663c56
XL
1839 in_block = true;
1840 }
1841 }
04454e1e 1842 token::OpenDelim(Delimiter::Bracket) => {
48663c56
XL
1843 bracket_depth += 1;
1844 self.bump();
1845 }
04454e1e 1846 token::CloseDelim(Delimiter::Brace) => {
48663c56
XL
1847 if brace_depth == 0 {
1848 debug!("recover_stmt_ return - close delim {:?}", self.token);
1849 break;
1850 }
1851 brace_depth -= 1;
1852 self.bump();
1853 if in_block && bracket_depth == 0 && brace_depth == 0 {
1854 debug!("recover_stmt_ return - block end {:?}", self.token);
1855 break;
1856 }
1857 }
04454e1e 1858 token::CloseDelim(Delimiter::Bracket) => {
48663c56
XL
1859 bracket_depth -= 1;
1860 if bracket_depth < 0 {
1861 bracket_depth = 0;
1862 }
1863 self.bump();
1864 }
1865 token::Eof => {
1866 debug!("recover_stmt_ return - Eof");
1867 break;
1868 }
1869 token::Semi => {
1870 self.bump();
dfeec247
XL
1871 if break_on_semi == SemiColonMode::Break
1872 && brace_depth == 0
1873 && bracket_depth == 0
1874 {
48663c56
XL
1875 debug!("recover_stmt_ return - Semi");
1876 break;
1877 }
1878 }
dfeec247
XL
1879 token::Comma
1880 if break_on_semi == SemiColonMode::Comma
1881 && brace_depth == 0
1882 && bracket_depth == 0 =>
48663c56 1883 {
48663c56
XL
1884 break;
1885 }
dfeec247 1886 _ => self.bump(),
48663c56
XL
1887 }
1888 }
1889 }
1890
e74abb32 1891 pub(super) fn check_for_for_in_in_typo(&mut self, in_span: Span) {
dc9dc135
XL
1892 if self.eat_keyword(kw::In) {
1893 // a common typo: `for _ in in bar {}`
923072b8
FG
1894 self.sess.emit_err(InInTypo {
1895 span: self.prev_token.span,
1896 sugg_span: in_span.until(self.prev_token.span),
1897 });
dc9dc135
XL
1898 }
1899 }
1900
e74abb32 1901 pub(super) fn eat_incorrect_doc_comment_for_param_type(&mut self) {
3dfed10e 1902 if let token::DocComment(..) = self.token.kind {
2b03887a 1903 self.sess.emit_err(DocCommentOnParamType { span: self.token.span });
dc9dc135 1904 self.bump();
dfeec247 1905 } else if self.token == token::Pound
04454e1e 1906 && self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Bracket))
dfeec247 1907 {
dc9dc135
XL
1908 let lo = self.token.span;
1909 // Skip every token until next possible arg.
04454e1e 1910 while self.token != token::CloseDelim(Delimiter::Bracket) {
dc9dc135
XL
1911 self.bump();
1912 }
1913 let sp = lo.to(self.token.span);
1914 self.bump();
2b03887a 1915 self.sess.emit_err(AttributeOnParamType { span: sp });
dc9dc135
XL
1916 }
1917 }
1918
e74abb32 1919 pub(super) fn parameter_without_type(
dc9dc135 1920 &mut self,
5e7ed085 1921 err: &mut Diagnostic,
dc9dc135
XL
1922 pat: P<ast::Pat>,
1923 require_name: bool,
74b04a01 1924 first_param: bool,
dc9dc135
XL
1925 ) -> Option<Ident> {
1926 // If we find a pattern followed by an identifier, it could be an (incorrect)
1927 // C-style parameter declaration.
dfeec247 1928 if self.check_ident()
04454e1e
FG
1929 && self.look_ahead(1, |t| {
1930 *t == token::Comma || *t == token::CloseDelim(Delimiter::Parenthesis)
1931 })
dfeec247
XL
1932 {
1933 // `fn foo(String s) {}`
dc9dc135
XL
1934 let ident = self.parse_ident().unwrap();
1935 let span = pat.span.with_hi(ident.span.hi());
1936
1937 err.span_suggestion(
1938 span,
1939 "declare the type after the parameter binding",
923072b8 1940 "<identifier>: <type>",
dc9dc135
XL
1941 Applicability::HasPlaceholders,
1942 );
1943 return Some(ident);
6a06907d
XL
1944 } else if require_name
1945 && (self.token == token::Comma
1946 || self.token == token::Lt
04454e1e 1947 || self.token == token::CloseDelim(Delimiter::Parenthesis))
6a06907d
XL
1948 {
1949 let rfc_note = "anonymous parameters are removed in the 2018 edition (see RFC 1685)";
1950
c295e0f8
XL
1951 let (ident, self_sugg, param_sugg, type_sugg, self_span, param_span, type_span) =
1952 match pat.kind {
1953 PatKind::Ident(_, ident, _) => (
1954 ident,
923072b8 1955 "self: ",
c295e0f8 1956 ": TypeName".to_string(),
923072b8 1957 "_: ",
c295e0f8
XL
1958 pat.span.shrink_to_lo(),
1959 pat.span.shrink_to_hi(),
1960 pat.span.shrink_to_lo(),
1961 ),
1962 // Also catches `fn foo(&a)`.
1963 PatKind::Ref(ref inner_pat, mutab)
1964 if matches!(inner_pat.clone().into_inner().kind, PatKind::Ident(..)) =>
1965 {
1966 match inner_pat.clone().into_inner().kind {
1967 PatKind::Ident(_, ident, _) => {
1968 let mutab = mutab.prefix_str();
1969 (
1970 ident,
923072b8 1971 "self: ",
5e7ed085 1972 format!("{ident}: &{mutab}TypeName"),
923072b8 1973 "_: ",
c295e0f8
XL
1974 pat.span.shrink_to_lo(),
1975 pat.span,
1976 pat.span.shrink_to_lo(),
1977 )
1978 }
1979 _ => unreachable!(),
6a06907d 1980 }
6a06907d 1981 }
c295e0f8
XL
1982 _ => {
1983 // Otherwise, try to get a type and emit a suggestion.
1984 if let Some(ty) = pat.to_ty() {
1985 err.span_suggestion_verbose(
1986 pat.span,
1987 "explicitly ignore the parameter name",
1988 format!("_: {}", pprust::ty_to_string(&ty)),
1989 Applicability::MachineApplicable,
1990 );
1991 err.note(rfc_note);
1992 }
6a06907d 1993
c295e0f8
XL
1994 return None;
1995 }
1996 };
6a06907d
XL
1997
1998 // `fn foo(a, b) {}`, `fn foo(a<x>, b<y>) {}` or `fn foo(usize, usize) {}`
1999 if first_param {
dc9dc135 2000 err.span_suggestion(
c295e0f8 2001 self_span,
6a06907d
XL
2002 "if this is a `self` type, give it a parameter name",
2003 self_sugg,
2004 Applicability::MaybeIncorrect,
dc9dc135 2005 );
dc9dc135 2006 }
6a06907d
XL
2007 // Avoid suggesting that `fn foo(HashMap<u32>)` is fixed with a change to
2008 // `fn foo(HashMap: TypeName<u32>)`.
2009 if self.token != token::Lt {
2010 err.span_suggestion(
c295e0f8 2011 param_span,
6a06907d
XL
2012 "if this is a parameter name, give it a type",
2013 param_sugg,
2014 Applicability::HasPlaceholders,
2015 );
2016 }
2017 err.span_suggestion(
c295e0f8 2018 type_span,
6a06907d
XL
2019 "if this is a type, explicitly ignore the parameter name",
2020 type_sugg,
2021 Applicability::MachineApplicable,
2022 );
2023 err.note(rfc_note);
2024
2025 // Don't attempt to recover by using the `X` in `X<Y>` as the parameter name.
2026 return if self.token == token::Lt { None } else { Some(ident) };
dc9dc135
XL
2027 }
2028 None
2029 }
2030
e74abb32 2031 pub(super) fn recover_arg_parse(&mut self) -> PResult<'a, (P<ast::Pat>, P<ast::Ty>)> {
9ffffee4 2032 let pat = self.parse_pat_no_top_alt(Some(Expected::ArgumentName))?;
dc9dc135
XL
2033 self.expect(&token::Colon)?;
2034 let ty = self.parse_ty()?;
2035
2b03887a 2036 self.sess.emit_err(PatternMethodParamWithoutBody { span: pat.span });
dc9dc135
XL
2037
2038 // Pretend the pattern is `_`, to avoid duplicate errors from AST validation.
3dfed10e
XL
2039 let pat =
2040 P(Pat { kind: PatKind::Wild, span: pat.span, id: ast::DUMMY_NODE_ID, tokens: None });
dc9dc135
XL
2041 Ok((pat, ty))
2042 }
2043
74b04a01 2044 pub(super) fn recover_bad_self_param(&mut self, mut param: Param) -> PResult<'a, Param> {
2b03887a 2045 let span = param.pat.span;
e74abb32 2046 param.ty.kind = TyKind::Err;
2b03887a 2047 self.sess.emit_err(SelfParamNotFirst { span });
e1599b0c 2048 Ok(param)
dc9dc135
XL
2049 }
2050
04454e1e 2051 pub(super) fn consume_block(&mut self, delim: Delimiter, consume_close: ConsumeClosingDelim) {
48663c56
XL
2052 let mut brace_depth = 0;
2053 loop {
2054 if self.eat(&token::OpenDelim(delim)) {
2055 brace_depth += 1;
e74abb32 2056 } else if self.check(&token::CloseDelim(delim)) {
48663c56 2057 if brace_depth == 0 {
e74abb32
XL
2058 if let ConsumeClosingDelim::Yes = consume_close {
2059 // Some of the callers of this method expect to be able to parse the
2060 // closing delimiter themselves, so we leave it alone. Otherwise we advance
2061 // the parser.
2062 self.bump();
2063 }
48663c56
XL
2064 return;
2065 } else {
e74abb32 2066 self.bump();
48663c56
XL
2067 brace_depth -= 1;
2068 continue;
2069 }
04454e1e 2070 } else if self.token == token::Eof {
48663c56
XL
2071 return;
2072 } else {
2073 self.bump();
2074 }
2075 }
2076 }
2077
5e7ed085 2078 pub(super) fn expected_expression_found(&self) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
dc9dc135
XL
2079 let (span, msg) = match (&self.token.kind, self.subparser_name) {
2080 (&token::Eof, Some(origin)) => {
2b03887a 2081 let sp = self.prev_token.span.shrink_to_hi();
5e7ed085 2082 (sp, format!("expected expression, found end of {origin}"))
dc9dc135 2083 }
dfeec247
XL
2084 _ => (
2085 self.token.span,
2086 format!("expected expression, found {}", super::token_descr(&self.token),),
2087 ),
dc9dc135 2088 };
49aad941 2089 let mut err = self.struct_span_err(span, msg);
dc9dc135
XL
2090 let sp = self.sess.source_map().start_point(self.token.span);
2091 if let Some(sp) = self.sess.ambiguous_block_expr_parse.borrow().get(&sp) {
2b03887a 2092 err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
dc9dc135
XL
2093 }
2094 err.span_label(span, "expected expression");
2095 err
2096 }
2097
e74abb32
XL
2098 fn consume_tts(
2099 &mut self,
2100 mut acc: i64, // `i64` because malformed code can have more closing delims than opening.
2101 // Not using `FxHashMap` due to `token::TokenKind: !Eq + !Hash`.
2102 modifier: &[(token::TokenKind, i64)],
2103 ) {
2104 while acc > 0 {
2105 if let Some((_, val)) = modifier.iter().find(|(t, _)| *t == self.token.kind) {
2106 acc += *val;
2107 }
2108 if self.token.kind == token::Eof {
2109 break;
2110 }
2111 self.bump();
2112 }
2113 }
2114
60c5eb7d 2115 /// Replace duplicated recovered parameters with `_` pattern to avoid unnecessary errors.
dc9dc135
XL
2116 ///
2117 /// This is necessary because at this point we don't know whether we parsed a function with
e1599b0c 2118 /// anonymous parameters or a function with names but no types. In order to minimize
60c5eb7d 2119 /// unnecessary errors, we assume the parameters are in the shape of `fn foo(a, b, c)` where
e1599b0c 2120 /// the parameters are *names* (so we don't emit errors about not being able to find `b` in
dc9dc135 2121 /// the local scope), but if we find the same name multiple times, like in `fn foo(i8, i8)`,
e1599b0c 2122 /// we deduplicate them to not complain about duplicated parameter names.
9ffffee4 2123 pub(super) fn deduplicate_recovered_params_names(&self, fn_inputs: &mut ThinVec<Param>) {
dc9dc135
XL
2124 let mut seen_inputs = FxHashSet::default();
2125 for input in fn_inputs.iter_mut() {
dfeec247
XL
2126 let opt_ident = if let (PatKind::Ident(_, ident, _), TyKind::Err) =
2127 (&input.pat.kind, &input.ty.kind)
2128 {
dc9dc135
XL
2129 Some(*ident)
2130 } else {
2131 None
2132 };
2133 if let Some(ident) = opt_ident {
2134 if seen_inputs.contains(&ident) {
e74abb32 2135 input.pat.kind = PatKind::Wild;
dc9dc135
XL
2136 }
2137 seen_inputs.insert(ident);
2138 }
2139 }
2140 }
29967ef6
XL
2141
2142 /// Handle encountering a symbol in a generic argument list that is not a `,` or `>`. In this
2143 /// case, we emit an error and try to suggest enclosing a const argument in braces if it looks
2144 /// like the user has forgotten them.
2145 pub fn handle_ambiguous_unbraced_const_arg(
2146 &mut self,
9ffffee4 2147 args: &mut ThinVec<AngleBracketedArg>,
29967ef6
XL
2148 ) -> PResult<'a, bool> {
2149 // If we haven't encountered a closing `>`, then the argument is malformed.
2150 // It's likely that the user has written a const expression without enclosing it
2151 // in braces, so we try to recover here.
2152 let arg = args.pop().unwrap();
2153 // FIXME: for some reason using `unexpected` or `expected_one_of_not_found` has
2154 // adverse side-effects to subsequent errors and seems to advance the parser.
2155 // We are causing this error here exclusively in case that a `const` expression
2156 // could be recovered from the current parser state, even if followed by more
2157 // arguments after a comma.
2158 let mut err = self.struct_span_err(
2159 self.token.span,
49aad941 2160 format!("expected one of `,` or `>`, found {}", super::token_descr(&self.token)),
29967ef6
XL
2161 );
2162 err.span_label(self.token.span, "expected one of `,` or `>`");
2163 match self.recover_const_arg(arg.span(), err) {
2164 Ok(arg) => {
2165 args.push(AngleBracketedArg::Arg(arg));
2166 if self.eat(&token::Comma) {
2167 return Ok(true); // Continue
2168 }
2169 }
2170 Err(mut err) => {
2171 args.push(arg);
2172 // We will emit a more generic error later.
2173 err.delay_as_bug();
2174 }
2175 }
2176 return Ok(false); // Don't continue.
2177 }
2178
fc512014
XL
2179 /// Attempt to parse a generic const argument that has not been enclosed in braces.
2180 /// There are a limited number of expressions that are permitted without being encoded
2181 /// in braces:
2182 /// - Literals.
2183 /// - Single-segment paths (i.e. standalone generic const parameters).
2184 /// All other expressions that can be parsed will emit an error suggesting the expression be
2185 /// wrapped in braces.
29967ef6
XL
2186 pub fn handle_unambiguous_unbraced_const_arg(&mut self) -> PResult<'a, P<Expr>> {
2187 let start = self.token.span;
2188 let expr = self.parse_expr_res(Restrictions::CONST_EXPR, None).map_err(|mut err| {
2189 err.span_label(
2190 start.shrink_to_lo(),
2191 "while parsing a const generic argument starting here",
2192 );
2193 err
2194 })?;
2195 if !self.expr_is_valid_const_arg(&expr) {
2b03887a
FG
2196 self.sess.emit_err(ConstGenericWithoutBraces {
2197 span: expr.span,
2198 sugg: ConstGenericWithoutBracesSugg {
2199 left: expr.span.shrink_to_lo(),
2200 right: expr.span.shrink_to_hi(),
2201 },
2202 });
29967ef6
XL
2203 }
2204 Ok(expr)
2205 }
2206
5e7ed085
FG
2207 fn recover_const_param_decl(&mut self, ty_generics: Option<&Generics>) -> Option<GenericArg> {
2208 let snapshot = self.create_snapshot_for_diagnostic();
f2b60f7d 2209 let param = match self.parse_const_param(AttrVec::new()) {
3c0e092e 2210 Ok(param) => param,
5e7ed085 2211 Err(err) => {
3c0e092e 2212 err.cancel();
5e7ed085
FG
2213 self.restore_snapshot(snapshot);
2214 return None;
3c0e092e
XL
2215 }
2216 };
2b03887a
FG
2217
2218 let ident = param.ident.to_string();
2219 let sugg = match (ty_generics, self.sess.source_map().span_to_snippet(param.span())) {
2220 (Some(Generics { params, span: impl_generics, .. }), Ok(snippet)) => {
2221 Some(match &params[..] {
2222 [] => UnexpectedConstParamDeclarationSugg::AddParam {
2223 impl_generics: *impl_generics,
2224 incorrect_decl: param.span(),
2225 snippet,
2226 ident,
2227 },
2228 [.., generic] => UnexpectedConstParamDeclarationSugg::AppendParam {
2229 impl_generics_end: generic.span().shrink_to_hi(),
2230 incorrect_decl: param.span(),
2231 snippet,
2232 ident,
2233 },
2234 })
2235 }
2236 _ => None,
2237 };
2238 self.sess.emit_err(UnexpectedConstParamDeclaration { span: param.span(), sugg });
2239
3c0e092e 2240 let value = self.mk_expr_err(param.span());
5e7ed085 2241 Some(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value }))
3c0e092e
XL
2242 }
2243
2244 pub fn recover_const_param_declaration(
2245 &mut self,
2246 ty_generics: Option<&Generics>,
2247 ) -> PResult<'a, Option<GenericArg>> {
2248 // We have to check for a few different cases.
5e7ed085
FG
2249 if let Some(arg) = self.recover_const_param_decl(ty_generics) {
2250 return Ok(Some(arg));
3c0e092e
XL
2251 }
2252
2253 // We haven't consumed `const` yet.
2254 let start = self.token.span;
2255 self.bump(); // `const`
2256
2257 // Detect and recover from the old, pre-RFC2000 syntax for const generics.
2b03887a 2258 let mut err = UnexpectedConstInGenericParam { span: start, to_remove: None };
3c0e092e 2259 if self.check_const_arg() {
2b03887a
FG
2260 err.to_remove = Some(start.until(self.token.span));
2261 self.sess.emit_err(err);
3c0e092e
XL
2262 Ok(Some(GenericArg::Const(self.parse_const_arg()?)))
2263 } else {
2264 let after_kw_const = self.token.span;
2b03887a
FG
2265 self.recover_const_arg(after_kw_const, err.into_diagnostic(&self.sess.span_diagnostic))
2266 .map(Some)
3c0e092e
XL
2267 }
2268 }
2269
29967ef6
XL
2270 /// Try to recover from possible generic const argument without `{` and `}`.
2271 ///
2272 /// When encountering code like `foo::< bar + 3 >` or `foo::< bar - baz >` we suggest
2273 /// `foo::<{ bar + 3 }>` and `foo::<{ bar - baz }>`, respectively. We only provide a suggestion
2b03887a 2274 /// if we think that the resulting expression would be well formed.
29967ef6
XL
2275 pub fn recover_const_arg(
2276 &mut self,
2277 start: Span,
5e7ed085 2278 mut err: DiagnosticBuilder<'a, ErrorGuaranteed>,
29967ef6 2279 ) -> PResult<'a, GenericArg> {
5e7ed085 2280 let is_op_or_dot = AssocOp::from_token(&self.token)
29967ef6
XL
2281 .and_then(|op| {
2282 if let AssocOp::Greater
2283 | AssocOp::Less
2284 | AssocOp::ShiftRight
2285 | AssocOp::GreaterEqual
2286 // Don't recover from `foo::<bar = baz>`, because this could be an attempt to
2287 // assign a value to a defaulted generic parameter.
2288 | AssocOp::Assign
2289 | AssocOp::AssignOp(_) = op
2290 {
2291 None
2292 } else {
2293 Some(op)
2294 }
2295 })
5e7ed085
FG
2296 .is_some()
2297 || self.token.kind == TokenKind::Dot;
29967ef6
XL
2298 // This will be true when a trait object type `Foo +` or a path which was a `const fn` with
2299 // type params has been parsed.
2300 let was_op =
2301 matches!(self.prev_token.kind, token::BinOp(token::Plus | token::Shr) | token::Gt);
5e7ed085 2302 if !is_op_or_dot && !was_op {
29967ef6
XL
2303 // We perform these checks and early return to avoid taking a snapshot unnecessarily.
2304 return Err(err);
2305 }
5e7ed085
FG
2306 let snapshot = self.create_snapshot_for_diagnostic();
2307 if is_op_or_dot {
29967ef6
XL
2308 self.bump();
2309 }
2310 match self.parse_expr_res(Restrictions::CONST_EXPR, None) {
2311 Ok(expr) => {
c295e0f8
XL
2312 // Find a mistake like `MyTrait<Assoc == S::Assoc>`.
2313 if token::EqEq == snapshot.token.kind {
2314 err.span_suggestion(
2315 snapshot.token.span,
2316 "if you meant to use an associated type binding, replace `==` with `=`",
923072b8 2317 "=",
c295e0f8
XL
2318 Applicability::MaybeIncorrect,
2319 );
2320 let value = self.mk_expr_err(start.to(expr.span));
2321 err.emit();
2322 return Ok(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value }));
5e7ed085
FG
2323 } else if token::Colon == snapshot.token.kind
2324 && expr.span.lo() == snapshot.token.span.hi()
2325 && matches!(expr.kind, ExprKind::Path(..))
2326 {
2327 // Find a mistake like "foo::var:A".
2328 err.span_suggestion(
2329 snapshot.token.span,
2330 "write a path separator here",
923072b8 2331 "::",
5e7ed085
FG
2332 Applicability::MaybeIncorrect,
2333 );
2334 err.emit();
2335 return Ok(GenericArg::Type(self.mk_ty(start.to(expr.span), TyKind::Err)));
c295e0f8
XL
2336 } else if token::Comma == self.token.kind || self.token.kind.should_end_const_arg()
2337 {
29967ef6
XL
2338 // Avoid the following output by checking that we consumed a full const arg:
2339 // help: expressions must be enclosed in braces to be used as const generic
2340 // arguments
2341 // |
2342 // LL | let sr: Vec<{ (u32, _, _) = vec![] };
2343 // | ^ ^
5e7ed085 2344 return Ok(self.dummy_const_arg_needs_braces(err, start.to(expr.span)));
29967ef6
XL
2345 }
2346 }
5e7ed085 2347 Err(err) => {
29967ef6
XL
2348 err.cancel();
2349 }
2350 }
5e7ed085 2351 self.restore_snapshot(snapshot);
29967ef6
XL
2352 Err(err)
2353 }
fc512014 2354
9ffffee4
FG
2355 /// Try to recover from an unbraced const argument whose first token [could begin a type][ty].
2356 ///
2357 /// [ty]: token::Token::can_begin_type
2358 pub(crate) fn recover_unbraced_const_arg_that_can_begin_ty(
2359 &mut self,
2360 mut snapshot: SnapshotParser<'a>,
2361 ) -> Option<P<ast::Expr>> {
2362 match snapshot.parse_expr_res(Restrictions::CONST_EXPR, None) {
2363 // Since we don't know the exact reason why we failed to parse the type or the
2364 // expression, employ a simple heuristic to weed out some pathological cases.
2365 Ok(expr) if let token::Comma | token::Gt = snapshot.token.kind => {
2366 self.restore_snapshot(snapshot);
2367 Some(expr)
2368 }
2369 Ok(_) => None,
2370 Err(err) => {
2371 err.cancel();
2372 None
2373 }
2374 }
2375 }
2376
5e7ed085
FG
2377 /// Creates a dummy const argument, and reports that the expression must be enclosed in braces
2378 pub fn dummy_const_arg_needs_braces(
2379 &self,
2380 mut err: DiagnosticBuilder<'a, ErrorGuaranteed>,
2381 span: Span,
2382 ) -> GenericArg {
2383 err.multipart_suggestion(
2384 "expressions must be enclosed in braces to be used as const generic \
2385 arguments",
2386 vec![(span.shrink_to_lo(), "{ ".to_string()), (span.shrink_to_hi(), " }".to_string())],
2387 Applicability::MaybeIncorrect,
2388 );
2389 let value = self.mk_expr_err(span);
2390 err.emit();
2391 GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value })
2392 }
2393
a2a8927a
XL
2394 /// Some special error handling for the "top-level" patterns in a match arm,
2395 /// `for` loop, `let`, &c. (in contrast to subpatterns within such).
923072b8 2396 pub(crate) fn maybe_recover_colon_colon_in_pat_typo(
a2a8927a
XL
2397 &mut self,
2398 mut first_pat: P<Pat>,
9ffffee4 2399 expected: Option<Expected>,
a2a8927a 2400 ) -> P<Pat> {
923072b8 2401 if token::Colon != self.token.kind {
a2a8927a
XL
2402 return first_pat;
2403 }
2404 if !matches!(first_pat.kind, PatKind::Ident(_, _, None) | PatKind::Path(..))
2405 || !self.look_ahead(1, |token| token.is_ident() && !token.is_reserved_ident())
2406 {
9ffffee4
FG
2407 let mut snapshot_type = self.create_snapshot_for_diagnostic();
2408 snapshot_type.bump(); // `:`
2409 match snapshot_type.parse_ty() {
2410 Err(inner_err) => {
2411 inner_err.cancel();
2412 }
2413 Ok(ty) => {
2414 let Err(mut err) = self.expected_one_of_not_found(&[], &[]) else {
2415 return first_pat;
2416 };
2417 err.span_label(ty.span, "specifying the type of a pattern isn't supported");
2418 self.restore_snapshot(snapshot_type);
2419 let span = first_pat.span.to(ty.span);
2420 first_pat = self.mk_pat(span, PatKind::Wild);
2421 err.emit();
2422 }
2423 }
a2a8927a
XL
2424 return first_pat;
2425 }
2426 // The pattern looks like it might be a path with a `::` -> `:` typo:
2427 // `match foo { bar:baz => {} }`
9ffffee4 2428 let colon_span = self.token.span;
a2a8927a
XL
2429 // We only emit "unexpected `:`" error here if we can successfully parse the
2430 // whole pattern correctly in that case.
9ffffee4
FG
2431 let mut snapshot_pat = self.create_snapshot_for_diagnostic();
2432 let mut snapshot_type = self.create_snapshot_for_diagnostic();
a2a8927a
XL
2433
2434 // Create error for "unexpected `:`".
2435 match self.expected_one_of_not_found(&[], &[]) {
2436 Err(mut err) => {
9ffffee4
FG
2437 // Skip the `:`.
2438 snapshot_pat.bump();
2439 snapshot_type.bump();
2440 match snapshot_pat.parse_pat_no_top_alt(expected) {
5e7ed085 2441 Err(inner_err) => {
a2a8927a 2442 inner_err.cancel();
a2a8927a
XL
2443 }
2444 Ok(mut pat) => {
2445 // We've parsed the rest of the pattern.
2446 let new_span = first_pat.span.to(pat.span);
2447 let mut show_sugg = false;
2448 // Try to construct a recovered pattern.
2449 match &mut pat.kind {
2450 PatKind::Struct(qself @ None, path, ..)
2451 | PatKind::TupleStruct(qself @ None, path, _)
2452 | PatKind::Path(qself @ None, path) => match &first_pat.kind {
2453 PatKind::Ident(_, ident, _) => {
5099ac24 2454 path.segments.insert(0, PathSegment::from_ident(*ident));
a2a8927a
XL
2455 path.span = new_span;
2456 show_sugg = true;
2457 first_pat = pat;
2458 }
2459 PatKind::Path(old_qself, old_path) => {
2460 path.segments = old_path
2461 .segments
2462 .iter()
2463 .cloned()
2464 .chain(take(&mut path.segments))
2465 .collect();
2466 path.span = new_span;
2467 *qself = old_qself.clone();
2468 first_pat = pat;
2469 show_sugg = true;
2470 }
2471 _ => {}
2472 },
f2b60f7d 2473 PatKind::Ident(BindingAnnotation::NONE, ident, None) => {
a2a8927a
XL
2474 match &first_pat.kind {
2475 PatKind::Ident(_, old_ident, _) => {
2476 let path = PatKind::Path(
2477 None,
2478 Path {
2479 span: new_span,
487cf647 2480 segments: thin_vec![
5099ac24
FG
2481 PathSegment::from_ident(*old_ident),
2482 PathSegment::from_ident(*ident),
a2a8927a
XL
2483 ],
2484 tokens: None,
2485 },
2486 );
2487 first_pat = self.mk_pat(new_span, path);
2488 show_sugg = true;
2489 }
2490 PatKind::Path(old_qself, old_path) => {
2491 let mut segments = old_path.segments.clone();
5099ac24 2492 segments.push(PathSegment::from_ident(*ident));
a2a8927a
XL
2493 let path = PatKind::Path(
2494 old_qself.clone(),
2495 Path { span: new_span, segments, tokens: None },
2496 );
2497 first_pat = self.mk_pat(new_span, path);
2498 show_sugg = true;
2499 }
2500 _ => {}
2501 }
2502 }
2503 _ => {}
2504 }
2505 if show_sugg {
9ffffee4
FG
2506 err.span_suggestion_verbose(
2507 colon_span.until(self.look_ahead(1, |t| t.span)),
a2a8927a 2508 "maybe write a path separator here",
923072b8 2509 "::",
a2a8927a
XL
2510 Applicability::MaybeIncorrect,
2511 );
2512 } else {
2513 first_pat = self.mk_pat(new_span, PatKind::Wild);
2514 }
9ffffee4
FG
2515 self.restore_snapshot(snapshot_pat);
2516 }
2517 }
2518 match snapshot_type.parse_ty() {
2519 Err(inner_err) => {
2520 inner_err.cancel();
2521 }
2522 Ok(ty) => {
2523 err.span_label(ty.span, "specifying the type of a pattern isn't supported");
2524 self.restore_snapshot(snapshot_type);
2525 let new_span = first_pat.span.to(ty.span);
2526 first_pat = self.mk_pat(new_span, PatKind::Wild);
a2a8927a
XL
2527 }
2528 }
9ffffee4 2529 err.emit();
a2a8927a
XL
2530 }
2531 _ => {
2532 // Carry on as if we had not done anything. This should be unreachable.
a2a8927a
XL
2533 }
2534 };
2535 first_pat
2536 }
2537
923072b8 2538 pub(crate) fn maybe_recover_unexpected_block_label(&mut self) -> bool {
2b03887a
FG
2539 // Check for `'a : {`
2540 if !(self.check_lifetime()
2541 && self.look_ahead(1, |tok| tok.kind == token::Colon)
2542 && self.look_ahead(2, |tok| tok.kind == token::OpenDelim(Delimiter::Brace)))
2543 {
5e7ed085 2544 return false;
2b03887a
FG
2545 }
2546 let label = self.eat_label().expect("just checked if a label exists");
2547 self.bump(); // eat `:`
5e7ed085
FG
2548 let span = label.ident.span.to(self.prev_token.span);
2549 let mut err = self.struct_span_err(span, "block label not supported here");
2550 err.span_label(span, "not supported here");
2551 err.tool_only_span_suggestion(
2552 label.ident.span.until(self.token.span),
2553 "remove this block label",
923072b8 2554 "",
5e7ed085
FG
2555 Applicability::MachineApplicable,
2556 );
2557 err.emit();
2558 true
2559 }
2560
a2a8927a
XL
2561 /// Some special error handling for the "top-level" patterns in a match arm,
2562 /// `for` loop, `let`, &c. (in contrast to subpatterns within such).
923072b8 2563 pub(crate) fn maybe_recover_unexpected_comma(
a2a8927a
XL
2564 &mut self,
2565 lo: Span,
5e7ed085 2566 rt: CommaRecoveryMode,
a2a8927a 2567 ) -> PResult<'a, ()> {
923072b8 2568 if self.token != token::Comma {
a2a8927a
XL
2569 return Ok(());
2570 }
2571
2572 // An unexpected comma after a top-level pattern is a clue that the
2573 // user (perhaps more accustomed to some other language) forgot the
2574 // parentheses in what should have been a tuple pattern; return a
2575 // suggestion-enhanced error here rather than choking on the comma later.
2576 let comma_span = self.token.span;
2577 self.bump();
5e7ed085 2578 if let Err(err) = self.skip_pat_list() {
a2a8927a
XL
2579 // We didn't expect this to work anyway; we just wanted to advance to the
2580 // end of the comma-sequence so we know the span to suggest parenthesizing.
2581 err.cancel();
2582 }
2583 let seq_span = lo.to(self.prev_token.span);
2584 let mut err = self.struct_span_err(comma_span, "unexpected `,` in pattern");
2585 if let Ok(seq_snippet) = self.span_to_snippet(seq_span) {
5e7ed085 2586 err.multipart_suggestion(
49aad941 2587 format!(
5e7ed085
FG
2588 "try adding parentheses to match on a tuple{}",
2589 if let CommaRecoveryMode::LikelyTuple = rt { "" } else { "..." },
2590 ),
2591 vec![
2592 (seq_span.shrink_to_lo(), "(".to_string()),
2593 (seq_span.shrink_to_hi(), ")".to_string()),
2594 ],
a2a8927a
XL
2595 Applicability::MachineApplicable,
2596 );
5e7ed085
FG
2597 if let CommaRecoveryMode::EitherTupleOrPipe = rt {
2598 err.span_suggestion(
2599 seq_span,
2600 "...or a vertical bar to match on multiple alternatives",
2601 seq_snippet.replace(',', " |"),
2602 Applicability::MachineApplicable,
2603 );
2604 }
a2a8927a
XL
2605 }
2606 Err(err)
2607 }
2608
923072b8 2609 pub(crate) fn maybe_recover_bounds_doubled_colon(&mut self, ty: &Ty) -> PResult<'a, ()> {
5e7ed085
FG
2610 let TyKind::Path(qself, path) = &ty.kind else { return Ok(()) };
2611 let qself_position = qself.as_ref().map(|qself| qself.position);
2612 for (i, segments) in path.segments.windows(2).enumerate() {
49aad941 2613 if qself_position.is_some_and(|pos| i < pos) {
5e7ed085
FG
2614 continue;
2615 }
2616 if let [a, b] = segments {
2617 let (a_span, b_span) = (a.span(), b.span());
2618 let between_span = a_span.shrink_to_hi().to(b_span.shrink_to_lo());
487cf647 2619 if self.span_to_snippet(between_span).as_deref() == Ok(":: ") {
2b03887a
FG
2620 return Err(DoubleColonInBound {
2621 span: path.span.shrink_to_hi(),
2622 between: between_span,
2623 }
2624 .into_diagnostic(&self.sess.span_diagnostic));
5e7ed085
FG
2625 }
2626 }
2627 }
2628 Ok(())
2629 }
2630
9c376795
FG
2631 pub fn is_diff_marker(&mut self, long_kind: &TokenKind, short_kind: &TokenKind) -> bool {
2632 (0..3).all(|i| self.look_ahead(i, |tok| tok == long_kind))
2633 && self.look_ahead(3, |tok| tok == short_kind)
2634 }
2635
2636 fn diff_marker(&mut self, long_kind: &TokenKind, short_kind: &TokenKind) -> Option<Span> {
2637 if self.is_diff_marker(long_kind, short_kind) {
2638 let lo = self.token.span;
2639 for _ in 0..4 {
2640 self.bump();
2641 }
2642 return Some(lo.to(self.prev_token.span));
2643 }
2644 None
2645 }
2646
2647 pub fn recover_diff_marker(&mut self) {
2648 let Some(start) = self.diff_marker(&TokenKind::BinOp(token::Shl), &TokenKind::Lt) else {
2649 return;
2650 };
2651 let mut spans = Vec::with_capacity(3);
2652 spans.push(start);
2653 let mut middlediff3 = None;
2654 let mut middle = None;
2655 let mut end = None;
2656 loop {
2657 if self.token.kind == TokenKind::Eof {
2658 break;
2659 }
2660 if let Some(span) = self.diff_marker(&TokenKind::OrOr, &TokenKind::BinOp(token::Or)) {
2661 middlediff3 = Some(span);
2662 }
2663 if let Some(span) = self.diff_marker(&TokenKind::EqEq, &TokenKind::Eq) {
2664 middle = Some(span);
2665 }
2666 if let Some(span) = self.diff_marker(&TokenKind::BinOp(token::Shr), &TokenKind::Gt) {
2667 spans.push(span);
2668 end = Some(span);
2669 break;
2670 }
2671 self.bump();
2672 }
2673 let mut err = self.struct_span_err(spans, "encountered diff marker");
2674 err.span_label(start, "after this is the code before the merge");
2675 if let Some(middle) = middlediff3 {
2676 err.span_label(middle, "");
2677 }
2678 if let Some(middle) = middle {
2679 err.span_label(middle, "");
2680 }
2681 if let Some(end) = end {
2682 err.span_label(end, "above this are the incoming code changes");
2683 }
2684 err.help(
2685 "if you're having merge conflicts after pulling new code, the top section is the code \
2686 you already had and the bottom section is the remote code",
2687 );
2688 err.help(
2689 "if you're in the middle of a rebase, the top section is the code being rebased onto \
2690 and the bottom section is the code coming from the current commit being rebased",
2691 );
2692 err.note(
2693 "for an explanation on these markers from the `git` documentation, visit \
2694 <https://git-scm.com/book/en/v2/Git-Tools-Advanced-Merging#_checking_out_conflicts>",
2695 );
2696 err.emit();
2697 FatalError.raise()
2698 }
2699
a2a8927a
XL
2700 /// Parse and throw away a parenthesized comma separated
2701 /// sequence of patterns until `)` is reached.
2702 fn skip_pat_list(&mut self) -> PResult<'a, ()> {
04454e1e 2703 while !self.check(&token::CloseDelim(Delimiter::Parenthesis)) {
a2a8927a
XL
2704 self.parse_pat_no_top_alt(None)?;
2705 if !self.eat(&token::Comma) {
2706 return Ok(());
2707 }
2708 }
2709 Ok(())
2710 }
48663c56 2711}