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