1use std::mem::take;
2use std::ops::{Deref, DerefMut};
3
4use ast::token::IdentIsRaw;
5use rustc_ast as ast;
6use rustc_ast::ptr::P;
7use rustc_ast::token::{self, Delimiter, Lit, LitKind, Token, TokenKind};
8use rustc_ast::util::parser::AssocOp;
9use rustc_ast::{
10 AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode, Block,
11 BlockCheckMode, Expr, ExprKind, GenericArg, Generics, Item, ItemKind, Param, Pat, PatKind,
12 Path, PathSegment, QSelf, Recovered, Ty, TyKind,
13};
14use rustc_ast_pretty::pprust;
15use rustc_data_structures::fx::FxHashSet;
16use rustc_errors::{
17 Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, PResult, Subdiagnostic, Suggestions,
18 pluralize,
19};
20use rustc_session::errors::ExprParenthesesNeeded;
21use rustc_span::edit_distance::find_best_match_for_name;
22use rustc_span::source_map::Spanned;
23use rustc_span::symbol::used_keywords;
24use rustc_span::{BytePos, DUMMY_SP, Ident, Span, SpanSnippetError, Symbol, kw, sym};
25use thin_vec::{ThinVec, thin_vec};
26use tracing::{debug, trace};
27
28use super::pat::Expected;
29use super::{
30 BlockMode, CommaRecoveryMode, ExpTokenPair, Parser, PathStyle, Restrictions, SemiColonMode,
31 SeqSep, TokenType,
32};
33use crate::errors::{
34 AddParen, AmbiguousPlus, AsyncMoveBlockIn2015, AsyncUseBlockIn2015, AttributeOnParamType,
35 AwaitSuggestion, BadQPathStage2, BadTypePlus, BadTypePlusSub, ColonAsSemi,
36 ComparisonOperatorsCannotBeChained, ComparisonOperatorsCannotBeChainedSugg,
37 ConstGenericWithoutBraces, ConstGenericWithoutBracesSugg, DocCommentDoesNotDocumentAnything,
38 DocCommentOnParamType, DoubleColonInBound, ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg,
39 GenericParamsWithoutAngleBrackets, GenericParamsWithoutAngleBracketsSugg,
40 HelpIdentifierStartsWithNumber, HelpUseLatestEdition, InInTypo, IncorrectAwait,
41 IncorrectSemicolon, IncorrectUseOfAwait, IncorrectUseOfUse, PatternMethodParamWithoutBody,
42 QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst, StructLiteralBodyWithoutPath,
43 StructLiteralBodyWithoutPathSugg, SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma,
44 TernaryOperator, UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration,
45 UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead, WrapType,
46};
47use crate::parser::attr::InnerAttrPolicy;
48use crate::{exp, fluent_generated as fluent};
49
50pub(super) fn dummy_arg(ident: Ident, guar: ErrorGuaranteed) -> Param {
52 let pat = P(Pat {
53 id: ast::DUMMY_NODE_ID,
54 kind: PatKind::Ident(BindingMode::NONE, ident, None),
55 span: ident.span,
56 tokens: None,
57 });
58 let ty = Ty { kind: TyKind::Err(guar), span: ident.span, id: ast::DUMMY_NODE_ID, tokens: None };
59 Param {
60 attrs: AttrVec::default(),
61 id: ast::DUMMY_NODE_ID,
62 pat,
63 span: ident.span,
64 ty: P(ty),
65 is_placeholder: false,
66 }
67}
68
69pub(super) trait RecoverQPath: Sized + 'static {
70 const PATH_STYLE: PathStyle = PathStyle::Expr;
71 fn to_ty(&self) -> Option<P<Ty>>;
72 fn recovered(qself: Option<P<QSelf>>, path: ast::Path) -> Self;
73}
74
75impl RecoverQPath for Ty {
76 const PATH_STYLE: PathStyle = PathStyle::Type;
77 fn to_ty(&self) -> Option<P<Ty>> {
78 Some(P(self.clone()))
79 }
80 fn recovered(qself: Option<P<QSelf>>, path: ast::Path) -> Self {
81 Self {
82 span: path.span,
83 kind: TyKind::Path(qself, path),
84 id: ast::DUMMY_NODE_ID,
85 tokens: None,
86 }
87 }
88}
89
90impl RecoverQPath for Pat {
91 const PATH_STYLE: PathStyle = PathStyle::Pat;
92 fn to_ty(&self) -> Option<P<Ty>> {
93 self.to_ty()
94 }
95 fn recovered(qself: Option<P<QSelf>>, path: ast::Path) -> Self {
96 Self {
97 span: path.span,
98 kind: PatKind::Path(qself, path),
99 id: ast::DUMMY_NODE_ID,
100 tokens: None,
101 }
102 }
103}
104
105impl RecoverQPath for Expr {
106 fn to_ty(&self) -> Option<P<Ty>> {
107 self.to_ty()
108 }
109 fn recovered(qself: Option<P<QSelf>>, path: ast::Path) -> Self {
110 Self {
111 span: path.span,
112 kind: ExprKind::Path(qself, path),
113 attrs: AttrVec::new(),
114 id: ast::DUMMY_NODE_ID,
115 tokens: None,
116 }
117 }
118}
119
120pub(crate) enum ConsumeClosingDelim {
122 Yes,
123 No,
124}
125
126#[derive(Clone, Copy)]
127pub enum AttemptLocalParseRecovery {
128 Yes,
129 No,
130}
131
132impl AttemptLocalParseRecovery {
133 pub(super) fn yes(&self) -> bool {
134 match self {
135 AttemptLocalParseRecovery::Yes => true,
136 AttemptLocalParseRecovery::No => false,
137 }
138 }
139
140 pub(super) fn no(&self) -> bool {
141 match self {
142 AttemptLocalParseRecovery::Yes => false,
143 AttemptLocalParseRecovery::No => true,
144 }
145 }
146}
147
148#[derive(Debug, Copy, Clone)]
151struct IncDecRecovery {
152 standalone: IsStandalone,
154 op: IncOrDec,
156 fixity: UnaryFixity,
158}
159
160#[derive(Debug, Copy, Clone)]
162enum IsStandalone {
163 Standalone,
165 Subexpr,
167}
168
169#[derive(Debug, Copy, Clone, PartialEq, Eq)]
170enum IncOrDec {
171 Inc,
172 Dec,
173}
174
175#[derive(Debug, Copy, Clone, PartialEq, Eq)]
176enum UnaryFixity {
177 Pre,
178 Post,
179}
180
181impl IncOrDec {
182 fn chr(&self) -> char {
183 match self {
184 Self::Inc => '+',
185 Self::Dec => '-',
186 }
187 }
188
189 fn name(&self) -> &'static str {
190 match self {
191 Self::Inc => "increment",
192 Self::Dec => "decrement",
193 }
194 }
195}
196
197impl std::fmt::Display for UnaryFixity {
198 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
199 match self {
200 Self::Pre => write!(f, "prefix"),
201 Self::Post => write!(f, "postfix"),
202 }
203 }
204}
205
206#[derive(Debug, rustc_macros::Subdiagnostic)]
207#[suggestion(
208 parse_misspelled_kw,
209 applicability = "machine-applicable",
210 code = "{similar_kw}",
211 style = "verbose"
212)]
213struct MisspelledKw {
214 similar_kw: String,
215 #[primary_span]
216 span: Span,
217 is_incorrect_case: bool,
218}
219
220fn find_similar_kw(lookup: Ident, candidates: &[Symbol]) -> Option<MisspelledKw> {
222 let lowercase = lookup.name.as_str().to_lowercase();
223 let lowercase_sym = Symbol::intern(&lowercase);
224 if candidates.contains(&lowercase_sym) {
225 Some(MisspelledKw { similar_kw: lowercase, span: lookup.span, is_incorrect_case: true })
226 } else if let Some(similar_sym) = find_best_match_for_name(candidates, lookup.name, None) {
227 Some(MisspelledKw {
228 similar_kw: similar_sym.to_string(),
229 span: lookup.span,
230 is_incorrect_case: false,
231 })
232 } else {
233 None
234 }
235}
236
237struct MultiSugg {
238 msg: String,
239 patches: Vec<(Span, String)>,
240 applicability: Applicability,
241}
242
243impl MultiSugg {
244 fn emit(self, err: &mut Diag<'_>) {
245 err.multipart_suggestion(self.msg, self.patches, self.applicability);
246 }
247
248 fn emit_verbose(self, err: &mut Diag<'_>) {
249 err.multipart_suggestion_verbose(self.msg, self.patches, self.applicability);
250 }
251}
252
253pub struct SnapshotParser<'a> {
257 parser: Parser<'a>,
258}
259
260impl<'a> Deref for SnapshotParser<'a> {
261 type Target = Parser<'a>;
262
263 fn deref(&self) -> &Self::Target {
264 &self.parser
265 }
266}
267
268impl<'a> DerefMut for SnapshotParser<'a> {
269 fn deref_mut(&mut self) -> &mut Self::Target {
270 &mut self.parser
271 }
272}
273
274impl<'a> Parser<'a> {
275 pub fn dcx(&self) -> DiagCtxtHandle<'a> {
276 self.psess.dcx()
277 }
278
279 pub(super) fn restore_snapshot(&mut self, snapshot: SnapshotParser<'a>) {
281 *self = snapshot.parser;
282 }
283
284 pub fn create_snapshot_for_diagnostic(&self) -> SnapshotParser<'a> {
286 let snapshot = self.clone();
287 SnapshotParser { parser: snapshot }
288 }
289
290 pub(super) fn span_to_snippet(&self, span: Span) -> Result<String, SpanSnippetError> {
291 self.psess.source_map().span_to_snippet(span)
292 }
293
294 pub(super) fn expected_ident_found(
298 &mut self,
299 recover: bool,
300 ) -> PResult<'a, (Ident, IdentIsRaw)> {
301 let valid_follow = &[
302 TokenKind::Eq,
303 TokenKind::Colon,
304 TokenKind::Comma,
305 TokenKind::Semi,
306 TokenKind::PathSep,
307 TokenKind::OpenDelim(Delimiter::Brace),
308 TokenKind::OpenDelim(Delimiter::Parenthesis),
309 TokenKind::CloseDelim(Delimiter::Brace),
310 TokenKind::CloseDelim(Delimiter::Parenthesis),
311 ];
312 if let TokenKind::DocComment(..) = self.prev_token.kind
313 && valid_follow.contains(&self.token.kind)
314 {
315 let err = self.dcx().create_err(DocCommentDoesNotDocumentAnything {
316 span: self.prev_token.span,
317 missing_comma: None,
318 });
319 return Err(err);
320 }
321
322 let mut recovered_ident = None;
323 let bad_token = self.token.clone();
326
327 let suggest_raw = if let Some((ident, IdentIsRaw::No)) = self.token.ident()
329 && ident.is_raw_guess()
330 && self.look_ahead(1, |t| valid_follow.contains(&t.kind))
331 {
332 recovered_ident = Some((ident, IdentIsRaw::Yes));
333
334 let ident_name = ident.name.to_string();
337
338 Some(SuggEscapeIdentifier { span: ident.span.shrink_to_lo(), ident_name })
339 } else {
340 None
341 };
342
343 let suggest_remove_comma =
344 if self.token == token::Comma && self.look_ahead(1, |t| t.is_ident()) {
345 if recover {
346 self.bump();
347 recovered_ident = self.ident_or_err(false).ok();
348 };
349
350 Some(SuggRemoveComma { span: bad_token.span })
351 } else {
352 None
353 };
354
355 let help_cannot_start_number = self.is_lit_bad_ident().map(|(len, valid_portion)| {
356 let (invalid, valid) = self.token.span.split_at(len as u32);
357
358 recovered_ident = Some((Ident::new(valid_portion, valid), IdentIsRaw::No));
359
360 HelpIdentifierStartsWithNumber { num_span: invalid }
361 });
362
363 let err = ExpectedIdentifier {
364 span: bad_token.span,
365 token: bad_token,
366 suggest_raw,
367 suggest_remove_comma,
368 help_cannot_start_number,
369 };
370 let mut err = self.dcx().create_err(err);
371
372 if self.token == token::Lt {
376 let valid_prev_keywords =
378 [kw::Fn, kw::Type, kw::Struct, kw::Enum, kw::Union, kw::Trait];
379
380 let maybe_keyword = self.prev_token.clone();
386 if valid_prev_keywords.into_iter().any(|x| maybe_keyword.is_keyword(x)) {
387 match self.parse_generics() {
390 Ok(generic) => {
391 if let TokenKind::Ident(symbol, _) = maybe_keyword.kind {
392 let ident_name = symbol;
393 if !self.look_ahead(1, |t| *t == token::Lt)
399 && let Ok(snippet) =
400 self.psess.source_map().span_to_snippet(generic.span)
401 {
402 err.multipart_suggestion_verbose(
403 format!("place the generic parameter name after the {ident_name} name"),
404 vec![
405 (self.token.span.shrink_to_hi(), snippet),
406 (generic.span, String::new())
407 ],
408 Applicability::MaybeIncorrect,
409 );
410 } else {
411 err.help(format!(
412 "place the generic parameter name after the {ident_name} name"
413 ));
414 }
415 }
416 }
417 Err(err) => {
418 err.cancel();
422 }
423 }
424 }
425 }
426
427 if let Some(recovered_ident) = recovered_ident
428 && recover
429 {
430 err.emit();
431 Ok(recovered_ident)
432 } else {
433 Err(err)
434 }
435 }
436
437 pub(super) fn expected_ident_found_err(&mut self) -> Diag<'a> {
438 self.expected_ident_found(false).unwrap_err()
439 }
440
441 pub(super) fn is_lit_bad_ident(&mut self) -> Option<(usize, Symbol)> {
447 if let token::Literal(Lit {
451 kind: token::LitKind::Integer | token::LitKind::Float,
452 symbol,
453 suffix: Some(suffix), }) = self.token.kind
455 && rustc_ast::MetaItemLit::from_token(&self.token).is_none()
456 {
457 Some((symbol.as_str().len(), suffix))
458 } else {
459 None
460 }
461 }
462
463 pub(super) fn expected_one_of_not_found(
464 &mut self,
465 edible: &[ExpTokenPair<'_>],
466 inedible: &[ExpTokenPair<'_>],
467 ) -> PResult<'a, ErrorGuaranteed> {
468 debug!("expected_one_of_not_found(edible: {:?}, inedible: {:?})", edible, inedible);
469 fn tokens_to_string(tokens: &[TokenType]) -> String {
470 let mut i = tokens.iter();
471 let b = i.next().map_or_else(String::new, |t| t.to_string());
473 i.enumerate().fold(b, |mut b, (i, a)| {
474 if tokens.len() > 2 && i == tokens.len() - 2 {
475 b.push_str(", or ");
476 } else if tokens.len() == 2 && i == tokens.len() - 2 {
477 b.push_str(" or ");
478 } else {
479 b.push_str(", ");
480 }
481 b.push_str(&a.to_string());
482 b
483 })
484 }
485
486 for exp in edible.iter().chain(inedible.iter()) {
487 self.expected_token_types.insert(exp.token_type);
488 }
489 let mut expected: Vec<_> = self.expected_token_types.iter().collect();
490 expected.sort_by_cached_key(|x| x.to_string());
491 expected.dedup();
492
493 let sm = self.psess.source_map();
494
495 if expected.contains(&TokenType::Semi) {
497 if self.prev_token == token::Question
500 && let Err(e) = self.maybe_recover_from_ternary_operator()
501 {
502 return Err(e);
503 }
504
505 if self.token.span == DUMMY_SP || self.prev_token.span == DUMMY_SP {
506 } else if !sm.is_multiline(self.prev_token.span.until(self.token.span)) {
508 } else if [token::Comma, token::Colon].contains(&self.token.kind)
510 && self.prev_token == token::CloseDelim(Delimiter::Parenthesis)
511 {
512 } else if self.look_ahead(1, |t| {
521 t == &token::CloseDelim(Delimiter::Brace)
522 || t.can_begin_expr() && *t != token::Colon
523 }) && [token::Comma, token::Colon].contains(&self.token.kind)
524 {
525 let guar = self.dcx().emit_err(ExpectedSemi {
532 span: self.token.span,
533 token: self.token.clone(),
534 unexpected_token_label: None,
535 sugg: ExpectedSemiSugg::ChangeToSemi(self.token.span),
536 });
537 self.bump();
538 return Ok(guar);
539 } else if self.look_ahead(0, |t| {
540 t == &token::CloseDelim(Delimiter::Brace)
541 || ((t.can_begin_expr() || t.can_begin_item())
542 && t != &token::Semi
543 && t != &token::Pound)
544 || (sm.is_multiline(
546 self.prev_token.span.shrink_to_hi().until(self.token.span.shrink_to_lo()),
547 ) && t == &token::Pound)
548 }) && !expected.contains(&TokenType::Comma)
549 {
550 let span = self.prev_token.span.shrink_to_hi();
556 let guar = self.dcx().emit_err(ExpectedSemi {
557 span,
558 token: self.token.clone(),
559 unexpected_token_label: Some(self.token.span),
560 sugg: ExpectedSemiSugg::AddSemi(span),
561 });
562 return Ok(guar);
563 }
564 }
565
566 if self.token == TokenKind::EqEq
567 && self.prev_token.is_ident()
568 && expected.contains(&TokenType::Eq)
569 {
570 return Err(self.dcx().create_err(UseEqInstead { span: self.token.span }));
572 }
573
574 if (self.token.is_keyword(kw::Move) || self.token.is_keyword(kw::Use))
575 && self.prev_token.is_keyword(kw::Async)
576 {
577 let span = self.prev_token.span.to(self.token.span);
579 if self.token.is_keyword(kw::Move) {
580 return Err(self.dcx().create_err(AsyncMoveBlockIn2015 { span }));
581 } else {
582 return Err(self.dcx().create_err(AsyncUseBlockIn2015 { span }));
584 }
585 }
586
587 let expect = tokens_to_string(&expected);
588 let actual = super::token_descr(&self.token);
589 let (msg_exp, (label_sp, label_exp)) = if expected.len() > 1 {
590 let fmt = format!("expected one of {expect}, found {actual}");
591 let short_expect = if expected.len() > 6 {
592 format!("{} possible tokens", expected.len())
593 } else {
594 expect
595 };
596 (fmt, (self.prev_token.span.shrink_to_hi(), format!("expected one of {short_expect}")))
597 } else if expected.is_empty() {
598 (
599 format!("unexpected token: {actual}"),
600 (self.prev_token.span, "unexpected token after this".to_string()),
601 )
602 } else {
603 (
604 format!("expected {expect}, found {actual}"),
605 (self.prev_token.span.shrink_to_hi(), format!("expected {expect}")),
606 )
607 };
608 self.last_unexpected_token_span = Some(self.token.span);
609 let mut err = self.dcx().struct_span_err(self.token.span, msg_exp);
611
612 if self.token == token::FatArrow
614 && expected.iter().any(|tok| matches!(tok, TokenType::Operator | TokenType::Le))
615 && !expected.iter().any(|tok| matches!(tok, TokenType::FatArrow | TokenType::Comma))
616 {
617 err.span_suggestion(
618 self.token.span,
619 "you might have meant to write a \"greater than or equal to\" comparison",
620 ">=",
621 Applicability::MaybeIncorrect,
622 );
623 }
624
625 if let TokenKind::Ident(symbol, _) = &self.prev_token.kind {
626 if ["def", "fun", "func", "function"].contains(&symbol.as_str()) {
627 err.span_suggestion_short(
628 self.prev_token.span,
629 format!("write `fn` instead of `{symbol}` to declare a function"),
630 "fn",
631 Applicability::MachineApplicable,
632 );
633 }
634 }
635
636 if let TokenKind::Ident(prev, _) = &self.prev_token.kind
637 && let TokenKind::Ident(cur, _) = &self.token.kind
638 {
639 let concat = Symbol::intern(&format!("{prev}{cur}"));
640 let ident = Ident::new(concat, DUMMY_SP);
641 if ident.is_used_keyword() || ident.is_reserved() || ident.is_raw_guess() {
642 let concat_span = self.prev_token.span.to(self.token.span);
643 err.span_suggestion_verbose(
644 concat_span,
645 format!("consider removing the space to spell keyword `{concat}`"),
646 concat,
647 Applicability::MachineApplicable,
648 );
649 }
650 }
651
652 if ((self.prev_token == TokenKind::Ident(sym::c, IdentIsRaw::No)
660 && matches!(&self.token.kind, TokenKind::Literal(token::Lit { kind: token::Str, .. })))
661 || (self.prev_token == TokenKind::Ident(sym::cr, IdentIsRaw::No)
662 && matches!(
663 &self.token.kind,
664 TokenKind::Literal(token::Lit { kind: token::Str, .. }) | token::Pound
665 )))
666 && self.prev_token.span.hi() == self.token.span.lo()
667 && !self.token.span.at_least_rust_2021()
668 {
669 err.note("you may be trying to write a c-string literal");
670 err.note("c-string literals require Rust 2021 or later");
671 err.subdiagnostic(HelpUseLatestEdition::new());
672 }
673
674 if self.prev_token.is_ident_named(sym::public)
676 && (self.token.can_begin_item()
677 || self.token == TokenKind::OpenDelim(Delimiter::Parenthesis))
678 {
679 err.span_suggestion_short(
680 self.prev_token.span,
681 "write `pub` instead of `public` to make the item public",
682 "pub",
683 Applicability::MachineApplicable,
684 );
685 }
686
687 if let token::DocComment(kind, style, _) = self.token.kind {
688 let pos = self.token.span.lo() + BytePos(2);
690 let span = self.token.span.with_lo(pos).with_hi(pos);
691 err.span_suggestion_verbose(
692 span,
693 format!(
694 "add a space before {} to write a regular comment",
695 match (kind, style) {
696 (token::CommentKind::Line, ast::AttrStyle::Inner) => "`!`",
697 (token::CommentKind::Block, ast::AttrStyle::Inner) => "`!`",
698 (token::CommentKind::Line, ast::AttrStyle::Outer) => "the last `/`",
699 (token::CommentKind::Block, ast::AttrStyle::Outer) => "the last `*`",
700 },
701 ),
702 " ".to_string(),
703 Applicability::MachineApplicable,
704 );
705 }
706
707 let sp = if self.token == token::Eof {
708 self.prev_token.span
710 } else {
711 label_sp
712 };
713
714 if self.check_too_many_raw_str_terminators(&mut err) {
715 if expected.contains(&TokenType::Semi) && self.eat(exp!(Semi)) {
716 let guar = err.emit();
717 return Ok(guar);
718 } else {
719 return Err(err);
720 }
721 }
722
723 if self.prev_token.span == DUMMY_SP {
724 err.span_label(self.token.span, label_exp);
727 } else if !sm.is_multiline(self.token.span.shrink_to_hi().until(sp.shrink_to_lo())) {
728 err.span_label(self.token.span, label_exp);
741 } else {
742 err.span_label(sp, label_exp);
743 err.span_label(self.token.span, "unexpected token");
744 }
745
746 if matches!(&err.suggestions, Suggestions::Enabled(list) if list.is_empty()) {
748 self.check_for_misspelled_kw(&mut err, &expected);
749 }
750 Err(err)
751 }
752
753 fn check_for_misspelled_kw(&self, err: &mut Diag<'_>, expected: &[TokenType]) {
756 let Some((curr_ident, _)) = self.token.ident() else {
757 return;
758 };
759 let expected_token_types: &[TokenType] =
760 expected.len().checked_sub(10).map_or(&expected, |index| &expected[index..]);
761 let expected_keywords: Vec<Symbol> =
762 expected_token_types.iter().filter_map(|token| token.is_keyword()).collect();
763
764 if !expected_keywords.is_empty()
769 && !curr_ident.is_used_keyword()
770 && let Some(misspelled_kw) = find_similar_kw(curr_ident, &expected_keywords)
771 {
772 err.subdiagnostic(misspelled_kw);
773 err.seal_suggestions();
776 } else if let Some((prev_ident, _)) = self.prev_token.ident()
777 && !prev_ident.is_used_keyword()
778 {
779 let all_keywords = used_keywords(|| prev_ident.span.edition());
784
785 if let Some(misspelled_kw) = find_similar_kw(prev_ident, &all_keywords) {
790 err.subdiagnostic(misspelled_kw);
791 err.seal_suggestions();
794 }
795 }
796 }
797
798 pub(super) fn attr_on_non_tail_expr(&self, expr: &Expr) -> ErrorGuaranteed {
800 let span = self.prev_token.span.shrink_to_hi();
802 let mut err = self.dcx().create_err(ExpectedSemi {
803 span,
804 token: self.token.clone(),
805 unexpected_token_label: Some(self.token.span),
806 sugg: ExpectedSemiSugg::AddSemi(span),
807 });
808 let attr_span = match &expr.attrs[..] {
809 [] => unreachable!(),
810 [only] => only.span,
811 [first, rest @ ..] => {
812 for attr in rest {
813 err.span_label(attr.span, "");
814 }
815 first.span
816 }
817 };
818 err.span_label(
819 attr_span,
820 format!(
821 "only `;` terminated statements or tail expressions are allowed after {}",
822 if expr.attrs.len() == 1 { "this attribute" } else { "these attributes" },
823 ),
824 );
825 if self.token == token::Pound
826 && self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Bracket))
827 {
828 err.span_label(span, "expected `;` here");
834 err.multipart_suggestion(
835 "alternatively, consider surrounding the expression with a block",
836 vec![
837 (expr.span.shrink_to_lo(), "{ ".to_string()),
838 (expr.span.shrink_to_hi(), " }".to_string()),
839 ],
840 Applicability::MachineApplicable,
841 );
842
843 let mut snapshot = self.create_snapshot_for_diagnostic();
845 if let [attr] = &expr.attrs[..]
846 && let ast::AttrKind::Normal(attr_kind) = &attr.kind
847 && let [segment] = &attr_kind.item.path.segments[..]
848 && segment.ident.name == sym::cfg
849 && let Some(args_span) = attr_kind.item.args.span()
850 && let next_attr = match snapshot.parse_attribute(InnerAttrPolicy::Forbidden(None))
851 {
852 Ok(next_attr) => next_attr,
853 Err(inner_err) => {
854 inner_err.cancel();
855 return err.emit();
856 }
857 }
858 && let ast::AttrKind::Normal(next_attr_kind) = next_attr.kind
859 && let Some(next_attr_args_span) = next_attr_kind.item.args.span()
860 && let [next_segment] = &next_attr_kind.item.path.segments[..]
861 && segment.ident.name == sym::cfg
862 {
863 let next_expr = match snapshot.parse_expr() {
864 Ok(next_expr) => next_expr,
865 Err(inner_err) => {
866 inner_err.cancel();
867 return err.emit();
868 }
869 };
870 let margin = self.psess.source_map().span_to_margin(next_expr.span).unwrap_or(0);
877 let sugg = vec![
878 (attr.span.with_hi(segment.span().hi()), "if cfg!".to_string()),
879 (args_span.shrink_to_hi().with_hi(attr.span.hi()), " {".to_string()),
880 (expr.span.shrink_to_lo(), " ".to_string()),
881 (
882 next_attr.span.with_hi(next_segment.span().hi()),
883 "} else if cfg!".to_string(),
884 ),
885 (
886 next_attr_args_span.shrink_to_hi().with_hi(next_attr.span.hi()),
887 " {".to_string(),
888 ),
889 (next_expr.span.shrink_to_lo(), " ".to_string()),
890 (next_expr.span.shrink_to_hi(), format!("\n{}}}", " ".repeat(margin))),
891 ];
892 err.multipart_suggestion(
893 "it seems like you are trying to provide different expressions depending on \
894 `cfg`, consider using `if cfg!(..)`",
895 sugg,
896 Applicability::MachineApplicable,
897 );
898 }
899 }
900
901 err.emit()
902 }
903
904 fn check_too_many_raw_str_terminators(&mut self, err: &mut Diag<'_>) -> bool {
905 let sm = self.psess.source_map();
906 match (&self.prev_token.kind, &self.token.kind) {
907 (
908 TokenKind::Literal(Lit {
909 kind: LitKind::StrRaw(n_hashes) | LitKind::ByteStrRaw(n_hashes),
910 ..
911 }),
912 TokenKind::Pound,
913 ) if !sm.is_multiline(
914 self.prev_token.span.shrink_to_hi().until(self.token.span.shrink_to_lo()),
915 ) =>
916 {
917 let n_hashes: u8 = *n_hashes;
918 err.primary_message("too many `#` when terminating raw string");
919 let str_span = self.prev_token.span;
920 let mut span = self.token.span;
921 let mut count = 0;
922 while self.token == TokenKind::Pound
923 && !sm.is_multiline(span.shrink_to_hi().until(self.token.span.shrink_to_lo()))
924 {
925 span = span.with_hi(self.token.span.hi());
926 self.bump();
927 count += 1;
928 }
929 err.span(span);
930 err.span_suggestion(
931 span,
932 format!("remove the extra `#`{}", pluralize!(count)),
933 "",
934 Applicability::MachineApplicable,
935 );
936 err.span_label(
937 str_span,
938 format!("this raw string started with {n_hashes} `#`{}", pluralize!(n_hashes)),
939 );
940 true
941 }
942 _ => false,
943 }
944 }
945
946 pub(super) fn maybe_suggest_struct_literal(
947 &mut self,
948 lo: Span,
949 s: BlockCheckMode,
950 maybe_struct_name: token::Token,
951 ) -> Option<PResult<'a, P<Block>>> {
952 if self.token.is_ident() && self.look_ahead(1, |t| t == &token::Colon) {
953 debug!(?maybe_struct_name, ?self.token);
958 let mut snapshot = self.create_snapshot_for_diagnostic();
959 let path = Path {
960 segments: ThinVec::new(),
961 span: self.prev_token.span.shrink_to_lo(),
962 tokens: None,
963 };
964 let struct_expr = snapshot.parse_expr_struct(None, path, false);
965 let block_tail = self.parse_block_tail(lo, s, AttemptLocalParseRecovery::No);
966 return Some(match (struct_expr, block_tail) {
967 (Ok(expr), Err(err)) => {
968 err.cancel();
977 self.restore_snapshot(snapshot);
978 let guar = self.dcx().emit_err(StructLiteralBodyWithoutPath {
979 span: expr.span,
980 sugg: StructLiteralBodyWithoutPathSugg {
981 before: expr.span.shrink_to_lo(),
982 after: expr.span.shrink_to_hi(),
983 },
984 });
985 Ok(self.mk_block(
986 thin_vec![self.mk_stmt_err(expr.span, guar)],
987 s,
988 lo.to(self.prev_token.span),
989 ))
990 }
991 (Err(err), Ok(tail)) => {
992 err.cancel();
994 Ok(tail)
995 }
996 (Err(snapshot_err), Err(err)) => {
997 snapshot_err.cancel();
999 self.consume_block(exp!(OpenBrace), exp!(CloseBrace), ConsumeClosingDelim::Yes);
1000 Err(err)
1001 }
1002 (Ok(_), Ok(tail)) => Ok(tail),
1003 });
1004 }
1005 None
1006 }
1007
1008 pub(super) fn recover_closure_body(
1009 &mut self,
1010 mut err: Diag<'a>,
1011 before: token::Token,
1012 prev: token::Token,
1013 token: token::Token,
1014 lo: Span,
1015 decl_hi: Span,
1016 ) -> PResult<'a, P<Expr>> {
1017 err.span_label(lo.to(decl_hi), "while parsing the body of this closure");
1018 let guar = match before.kind {
1019 token::OpenDelim(Delimiter::Brace)
1020 if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) =>
1021 {
1022 err.multipart_suggestion(
1024 "you might have meant to open the body of the closure, instead of enclosing \
1025 the closure in a block",
1026 vec![
1027 (before.span, String::new()),
1028 (prev.span.shrink_to_hi(), " {".to_string()),
1029 ],
1030 Applicability::MaybeIncorrect,
1031 );
1032 let guar = err.emit();
1033 self.eat_to_tokens(&[exp!(CloseBrace)]);
1034 guar
1035 }
1036 token::OpenDelim(Delimiter::Parenthesis)
1037 if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) =>
1038 {
1039 self.eat_to_tokens(&[exp!(CloseParen), exp!(Comma)]);
1042
1043 err.multipart_suggestion_verbose(
1044 "you might have meant to open the body of the closure",
1045 vec![
1046 (prev.span.shrink_to_hi(), " {".to_string()),
1047 (self.token.span.shrink_to_lo(), "}".to_string()),
1048 ],
1049 Applicability::MaybeIncorrect,
1050 );
1051 err.emit()
1052 }
1053 _ if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) => {
1054 err.multipart_suggestion_verbose(
1057 "you might have meant to open the body of the closure",
1058 vec![(prev.span.shrink_to_hi(), " {".to_string())],
1059 Applicability::HasPlaceholders,
1060 );
1061 return Err(err);
1062 }
1063 _ => return Err(err),
1064 };
1065 Ok(self.mk_expr_err(lo.to(self.token.span), guar))
1066 }
1067
1068 pub(super) fn eat_to_tokens(&mut self, closes: &[ExpTokenPair<'_>]) {
1071 if let Err(err) = self
1072 .parse_seq_to_before_tokens(closes, &[], SeqSep::none(), |p| Ok(p.parse_token_tree()))
1073 {
1074 err.cancel();
1075 }
1076 }
1077
1078 pub(super) fn check_trailing_angle_brackets(
1089 &mut self,
1090 segment: &PathSegment,
1091 end: &[ExpTokenPair<'_>],
1092 ) -> Option<ErrorGuaranteed> {
1093 if !self.may_recover() {
1094 return None;
1095 }
1096
1097 let parsed_angle_bracket_args =
1122 segment.args.as_ref().is_some_and(|args| args.is_angle_bracketed());
1123
1124 debug!(
1125 "check_trailing_angle_brackets: parsed_angle_bracket_args={:?}",
1126 parsed_angle_bracket_args,
1127 );
1128 if !parsed_angle_bracket_args {
1129 return None;
1130 }
1131
1132 let lo = self.token.span;
1135
1136 let mut position = 0;
1140
1141 let mut number_of_shr = 0;
1145 let mut number_of_gt = 0;
1146 while self.look_ahead(position, |t| {
1147 trace!("check_trailing_angle_brackets: t={:?}", t);
1148 if *t == token::Shr {
1149 number_of_shr += 1;
1150 true
1151 } else if *t == token::Gt {
1152 number_of_gt += 1;
1153 true
1154 } else {
1155 false
1156 }
1157 }) {
1158 position += 1;
1159 }
1160
1161 debug!(
1163 "check_trailing_angle_brackets: number_of_gt={:?} number_of_shr={:?}",
1164 number_of_gt, number_of_shr,
1165 );
1166 if number_of_gt < 1 && number_of_shr < 1 {
1167 return None;
1168 }
1169
1170 if self.look_ahead(position, |t| {
1173 trace!("check_trailing_angle_brackets: t={:?}", t);
1174 end.iter().any(|exp| exp.tok == &t.kind)
1175 }) {
1176 self.eat_to_tokens(end);
1179 let span = lo.to(self.prev_token.span);
1180
1181 let num_extra_brackets = number_of_gt + number_of_shr * 2;
1182 return Some(self.dcx().emit_err(UnmatchedAngleBrackets { span, num_extra_brackets }));
1183 }
1184 None
1185 }
1186
1187 pub(super) fn check_turbofish_missing_angle_brackets(&mut self, segment: &mut PathSegment) {
1190 if !self.may_recover() {
1191 return;
1192 }
1193
1194 if self.token == token::PathSep && segment.args.is_none() {
1195 let snapshot = self.create_snapshot_for_diagnostic();
1196 self.bump();
1197 let lo = self.token.span;
1198 match self.parse_angle_args(None) {
1199 Ok(args) => {
1200 let span = lo.to(self.prev_token.span);
1201 let mut trailing_span = self.prev_token.span.shrink_to_hi();
1203 while self.token == token::Shr || self.token == token::Gt {
1204 trailing_span = trailing_span.to(self.token.span);
1205 self.bump();
1206 }
1207 if self.token == token::OpenDelim(Delimiter::Parenthesis) {
1208 segment.args = Some(AngleBracketedArgs { args, span }.into());
1210
1211 self.dcx().emit_err(GenericParamsWithoutAngleBrackets {
1212 span,
1213 sugg: GenericParamsWithoutAngleBracketsSugg {
1214 left: span.shrink_to_lo(),
1215 right: trailing_span,
1216 },
1217 });
1218 } else {
1219 self.restore_snapshot(snapshot);
1221 }
1222 }
1223 Err(err) => {
1224 err.cancel();
1227 self.restore_snapshot(snapshot);
1228 }
1229 }
1230 }
1231 }
1232
1233 pub(super) fn check_mistyped_turbofish_with_multiple_type_params(
1236 &mut self,
1237 mut e: Diag<'a>,
1238 expr: &mut P<Expr>,
1239 ) -> PResult<'a, ErrorGuaranteed> {
1240 if let ExprKind::Binary(binop, _, _) = &expr.kind
1241 && let ast::BinOpKind::Lt = binop.node
1242 && self.eat(exp!(Comma))
1243 {
1244 let x = self.parse_seq_to_before_end(
1245 exp!(Gt),
1246 SeqSep::trailing_allowed(exp!(Comma)),
1247 |p| match p.parse_generic_arg(None)? {
1248 Some(arg) => Ok(arg),
1249 None => p.unexpected_any(),
1251 },
1252 );
1253 match x {
1254 Ok((_, _, Recovered::No)) => {
1255 if self.eat(exp!(Gt)) {
1256 e.span_suggestion_verbose(
1258 binop.span.shrink_to_lo(),
1259 fluent::parse_sugg_turbofish_syntax,
1260 "::",
1261 Applicability::MaybeIncorrect,
1262 );
1263 match self.parse_expr() {
1264 Ok(_) => {
1265 let guar = e.emit();
1269 *expr = self.mk_expr_err(expr.span.to(self.prev_token.span), guar);
1270 return Ok(guar);
1271 }
1272 Err(err) => {
1273 err.cancel();
1274 }
1275 }
1276 }
1277 }
1278 Ok((_, _, Recovered::Yes(_))) => {}
1279 Err(err) => {
1280 err.cancel();
1281 }
1282 }
1283 }
1284 Err(e)
1285 }
1286
1287 pub(super) fn suggest_add_missing_let_for_stmt(&mut self, err: &mut Diag<'a>) {
1290 if self.token == token::Colon {
1291 let prev_span = self.prev_token.span.shrink_to_lo();
1292 let snapshot = self.create_snapshot_for_diagnostic();
1293 self.bump();
1294 match self.parse_ty() {
1295 Ok(_) => {
1296 if self.token == token::Eq {
1297 let sugg = SuggAddMissingLetStmt { span: prev_span };
1298 sugg.add_to_diag(err);
1299 }
1300 }
1301 Err(e) => {
1302 e.cancel();
1303 }
1304 }
1305 self.restore_snapshot(snapshot);
1306 }
1307 }
1308
1309 fn attempt_chained_comparison_suggestion(
1313 &mut self,
1314 err: &mut ComparisonOperatorsCannotBeChained,
1315 inner_op: &Expr,
1316 outer_op: &Spanned<AssocOp>,
1317 ) -> bool {
1318 if let ExprKind::Binary(op, l1, r1) = &inner_op.kind {
1319 if let ExprKind::Field(_, ident) = l1.kind
1320 && !ident.is_numeric()
1321 && !matches!(r1.kind, ExprKind::Lit(_))
1322 {
1323 return false;
1326 }
1327 return match (op.node, &outer_op.node) {
1328 (BinOpKind::Eq, AssocOp::Binary(BinOpKind::Eq)) |
1330 (BinOpKind::Lt, AssocOp::Binary(BinOpKind::Lt | BinOpKind::Le)) |
1332 (BinOpKind::Le, AssocOp::Binary(BinOpKind::Lt | BinOpKind::Le)) |
1333 (BinOpKind::Gt, AssocOp::Binary(BinOpKind::Gt | BinOpKind::Ge)) |
1335 (BinOpKind::Ge, AssocOp::Binary(BinOpKind::Gt | BinOpKind::Ge)) => {
1336 let expr_to_str = |e: &Expr| {
1337 self.span_to_snippet(e.span)
1338 .unwrap_or_else(|_| pprust::expr_to_string(e))
1339 };
1340 err.chaining_sugg = Some(ComparisonOperatorsCannotBeChainedSugg::SplitComparison {
1341 span: inner_op.span.shrink_to_hi(),
1342 middle_term: expr_to_str(r1),
1343 });
1344 false }
1346 (
1348 BinOpKind::Eq,
1349 AssocOp::Binary(BinOpKind::Lt | BinOpKind::Le | BinOpKind::Gt | BinOpKind::Ge)
1350 ) => {
1351 let snapshot = self.create_snapshot_for_diagnostic();
1353 match self.parse_expr() {
1354 Ok(r2) => {
1355 err.chaining_sugg = Some(ComparisonOperatorsCannotBeChainedSugg::Parenthesize {
1358 left: r1.span.shrink_to_lo(),
1359 right: r2.span.shrink_to_hi(),
1360 });
1361 true
1362 }
1363 Err(expr_err) => {
1364 expr_err.cancel();
1365 self.restore_snapshot(snapshot);
1366 true
1367 }
1368 }
1369 }
1370 (
1372 BinOpKind::Lt | BinOpKind::Le | BinOpKind::Gt | BinOpKind::Ge,
1373 AssocOp::Binary(BinOpKind::Eq)
1374 ) => {
1375 let snapshot = self.create_snapshot_for_diagnostic();
1376 match self.parse_expr() {
1379 Ok(_) => {
1380 err.chaining_sugg = Some(ComparisonOperatorsCannotBeChainedSugg::Parenthesize {
1381 left: l1.span.shrink_to_lo(),
1382 right: r1.span.shrink_to_hi(),
1383 });
1384 true
1385 }
1386 Err(expr_err) => {
1387 expr_err.cancel();
1388 self.restore_snapshot(snapshot);
1389 false
1390 }
1391 }
1392 }
1393 _ => false
1394 };
1395 }
1396 false
1397 }
1398
1399 pub(super) fn check_no_chained_comparison(
1418 &mut self,
1419 inner_op: &Expr,
1420 outer_op: &Spanned<AssocOp>,
1421 ) -> PResult<'a, Option<P<Expr>>> {
1422 debug_assert!(
1423 outer_op.node.is_comparison(),
1424 "check_no_chained_comparison: {:?} is not comparison",
1425 outer_op.node,
1426 );
1427
1428 let mk_err_expr =
1429 |this: &Self, span, guar| Ok(Some(this.mk_expr(span, ExprKind::Err(guar))));
1430
1431 match &inner_op.kind {
1432 ExprKind::Binary(op, l1, r1) if op.node.is_comparison() => {
1433 let mut err = ComparisonOperatorsCannotBeChained {
1434 span: vec![op.span, self.prev_token.span],
1435 suggest_turbofish: None,
1436 help_turbofish: false,
1437 chaining_sugg: None,
1438 };
1439
1440 if op.node == BinOpKind::Lt && outer_op.node == AssocOp::Binary(BinOpKind::Lt)
1443 || outer_op.node == AssocOp::Binary(BinOpKind::Gt)
1444 {
1445 if outer_op.node == AssocOp::Binary(BinOpKind::Lt) {
1446 let snapshot = self.create_snapshot_for_diagnostic();
1447 self.bump();
1448 let modifiers = [(token::Lt, 1), (token::Gt, -1), (token::Shr, -2)];
1450 self.consume_tts(1, &modifiers);
1451
1452 if !&[token::OpenDelim(Delimiter::Parenthesis), token::PathSep]
1453 .contains(&self.token.kind)
1454 {
1455 self.restore_snapshot(snapshot);
1458 }
1459 }
1460 return if self.token == token::PathSep {
1461 if let ExprKind::Binary(o, ..) = inner_op.kind
1464 && o.node == BinOpKind::Lt
1465 {
1466 err.suggest_turbofish = Some(op.span.shrink_to_lo());
1467 } else {
1468 err.help_turbofish = true;
1469 }
1470
1471 let snapshot = self.create_snapshot_for_diagnostic();
1472 self.bump(); match self.parse_expr() {
1476 Ok(_) => {
1477 let guar = self.dcx().emit_err(err);
1479 mk_err_expr(self, inner_op.span.to(self.prev_token.span), guar)
1483 }
1484 Err(expr_err) => {
1485 expr_err.cancel();
1486 self.restore_snapshot(snapshot);
1489 Err(self.dcx().create_err(err))
1490 }
1491 }
1492 } else if self.token == token::OpenDelim(Delimiter::Parenthesis) {
1493 if let ExprKind::Binary(o, ..) = inner_op.kind
1496 && o.node == BinOpKind::Lt
1497 {
1498 err.suggest_turbofish = Some(op.span.shrink_to_lo());
1499 } else {
1500 err.help_turbofish = true;
1501 }
1502 match self.consume_fn_args() {
1504 Err(()) => Err(self.dcx().create_err(err)),
1505 Ok(()) => {
1506 let guar = self.dcx().emit_err(err);
1507 mk_err_expr(self, inner_op.span.to(self.prev_token.span), guar)
1511 }
1512 }
1513 } else {
1514 if !matches!(l1.kind, ExprKind::Lit(_))
1515 && !matches!(r1.kind, ExprKind::Lit(_))
1516 {
1517 err.help_turbofish = true;
1520 }
1521
1522 let recovered = self
1525 .attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op);
1526 if recovered {
1527 let guar = self.dcx().emit_err(err);
1528 mk_err_expr(self, inner_op.span.to(self.prev_token.span), guar)
1529 } else {
1530 Err(self.dcx().create_err(err))
1532 }
1533 };
1534 }
1535 let recovered =
1536 self.attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op);
1537 let guar = self.dcx().emit_err(err);
1538 if recovered {
1539 return mk_err_expr(self, inner_op.span.to(self.prev_token.span), guar);
1540 }
1541 }
1542 _ => {}
1543 }
1544 Ok(None)
1545 }
1546
1547 fn consume_fn_args(&mut self) -> Result<(), ()> {
1548 let snapshot = self.create_snapshot_for_diagnostic();
1549 self.bump(); let modifiers = [
1553 (token::OpenDelim(Delimiter::Parenthesis), 1),
1554 (token::CloseDelim(Delimiter::Parenthesis), -1),
1555 ];
1556 self.consume_tts(1, &modifiers);
1557
1558 if self.token == token::Eof {
1559 self.restore_snapshot(snapshot);
1561 Err(())
1562 } else {
1563 Ok(())
1565 }
1566 }
1567
1568 pub(super) fn maybe_report_ambiguous_plus(&mut self, impl_dyn_multi: bool, ty: &Ty) {
1569 if impl_dyn_multi {
1570 self.dcx().emit_err(AmbiguousPlus {
1571 span: ty.span,
1572 suggestion: AddParen { lo: ty.span.shrink_to_lo(), hi: ty.span.shrink_to_hi() },
1573 });
1574 }
1575 }
1576
1577 pub(super) fn maybe_recover_from_question_mark(&mut self, ty: P<Ty>) -> P<Ty> {
1579 if self.token == token::Question {
1580 self.bump();
1581 let guar = self.dcx().emit_err(QuestionMarkInType {
1582 span: self.prev_token.span,
1583 sugg: QuestionMarkInTypeSugg {
1584 left: ty.span.shrink_to_lo(),
1585 right: self.prev_token.span,
1586 },
1587 });
1588 self.mk_ty(ty.span.to(self.prev_token.span), TyKind::Err(guar))
1589 } else {
1590 ty
1591 }
1592 }
1593
1594 pub(super) fn maybe_recover_from_ternary_operator(&mut self) -> PResult<'a, ()> {
1598 if self.prev_token != token::Question {
1599 return PResult::Ok(());
1600 }
1601
1602 let lo = self.prev_token.span.lo();
1603 let snapshot = self.create_snapshot_for_diagnostic();
1604
1605 if match self.parse_expr() {
1606 Ok(_) => true,
1607 Err(err) => {
1608 err.cancel();
1609 self.token == token::Colon
1612 }
1613 } {
1614 if self.eat_noexpect(&token::Colon) {
1615 match self.parse_expr() {
1616 Ok(_) => {
1617 return Err(self
1618 .dcx()
1619 .create_err(TernaryOperator { span: self.token.span.with_lo(lo) }));
1620 }
1621 Err(err) => {
1622 err.cancel();
1623 }
1624 };
1625 }
1626 }
1627 self.restore_snapshot(snapshot);
1628 Ok(())
1629 }
1630
1631 pub(super) fn maybe_recover_from_bad_type_plus(&mut self, ty: &Ty) -> PResult<'a, ()> {
1632 if !self.token.is_like_plus() {
1634 return Ok(());
1635 }
1636
1637 self.bump(); let _bounds = self.parse_generic_bounds()?;
1639 let sum_span = ty.span.to(self.prev_token.span);
1640
1641 let sub = match &ty.kind {
1642 TyKind::Ref(_lifetime, mut_ty) => {
1643 let lo = mut_ty.ty.span.shrink_to_lo();
1644 let hi = self.prev_token.span.shrink_to_hi();
1645 BadTypePlusSub::AddParen { suggestion: AddParen { lo, hi } }
1646 }
1647 TyKind::Ptr(..) | TyKind::BareFn(..) => BadTypePlusSub::ForgotParen { span: sum_span },
1648 _ => BadTypePlusSub::ExpectPath { span: sum_span },
1649 };
1650
1651 self.dcx().emit_err(BadTypePlus { ty: pprust::ty_to_string(ty), span: sum_span, sub });
1652
1653 Ok(())
1654 }
1655
1656 pub(super) fn recover_from_prefix_increment(
1657 &mut self,
1658 operand_expr: P<Expr>,
1659 op_span: Span,
1660 start_stmt: bool,
1661 ) -> PResult<'a, P<Expr>> {
1662 let standalone = if start_stmt { IsStandalone::Standalone } else { IsStandalone::Subexpr };
1663 let kind = IncDecRecovery { standalone, op: IncOrDec::Inc, fixity: UnaryFixity::Pre };
1664 self.recover_from_inc_dec(operand_expr, kind, op_span)
1665 }
1666
1667 pub(super) fn recover_from_postfix_increment(
1668 &mut self,
1669 operand_expr: P<Expr>,
1670 op_span: Span,
1671 start_stmt: bool,
1672 ) -> PResult<'a, P<Expr>> {
1673 let kind = IncDecRecovery {
1674 standalone: if start_stmt { IsStandalone::Standalone } else { IsStandalone::Subexpr },
1675 op: IncOrDec::Inc,
1676 fixity: UnaryFixity::Post,
1677 };
1678 self.recover_from_inc_dec(operand_expr, kind, op_span)
1679 }
1680
1681 pub(super) fn recover_from_postfix_decrement(
1682 &mut self,
1683 operand_expr: P<Expr>,
1684 op_span: Span,
1685 start_stmt: bool,
1686 ) -> PResult<'a, P<Expr>> {
1687 let kind = IncDecRecovery {
1688 standalone: if start_stmt { IsStandalone::Standalone } else { IsStandalone::Subexpr },
1689 op: IncOrDec::Dec,
1690 fixity: UnaryFixity::Post,
1691 };
1692 self.recover_from_inc_dec(operand_expr, kind, op_span)
1693 }
1694
1695 fn recover_from_inc_dec(
1696 &mut self,
1697 base: P<Expr>,
1698 kind: IncDecRecovery,
1699 op_span: Span,
1700 ) -> PResult<'a, P<Expr>> {
1701 let mut err = self.dcx().struct_span_err(
1702 op_span,
1703 format!("Rust has no {} {} operator", kind.fixity, kind.op.name()),
1704 );
1705 err.span_label(op_span, format!("not a valid {} operator", kind.fixity));
1706
1707 let help_base_case = |mut err: Diag<'_, _>, base| {
1708 err.help(format!("use `{}= 1` instead", kind.op.chr()));
1709 err.emit();
1710 Ok(base)
1711 };
1712
1713 let spans = match kind.fixity {
1715 UnaryFixity::Pre => (op_span, base.span.shrink_to_hi()),
1716 UnaryFixity::Post => (base.span.shrink_to_lo(), op_span),
1717 };
1718
1719 match kind.standalone {
1720 IsStandalone::Standalone => {
1721 self.inc_dec_standalone_suggest(kind, spans).emit_verbose(&mut err)
1722 }
1723 IsStandalone::Subexpr => {
1724 let Ok(base_src) = self.span_to_snippet(base.span) else {
1725 return help_base_case(err, base);
1726 };
1727 match kind.fixity {
1728 UnaryFixity::Pre => {
1729 self.prefix_inc_dec_suggest(base_src, kind, spans).emit(&mut err)
1730 }
1731 UnaryFixity::Post => {
1732 if !matches!(base.kind, ExprKind::Binary(_, _, _)) {
1735 self.postfix_inc_dec_suggest(base_src, kind, spans).emit(&mut err)
1736 }
1737 }
1738 }
1739 }
1740 }
1741 Err(err)
1742 }
1743
1744 fn prefix_inc_dec_suggest(
1745 &mut self,
1746 base_src: String,
1747 kind: IncDecRecovery,
1748 (pre_span, post_span): (Span, Span),
1749 ) -> MultiSugg {
1750 MultiSugg {
1751 msg: format!("use `{}= 1` instead", kind.op.chr()),
1752 patches: vec![
1753 (pre_span, "{ ".to_string()),
1754 (post_span, format!(" {}= 1; {} }}", kind.op.chr(), base_src)),
1755 ],
1756 applicability: Applicability::MachineApplicable,
1757 }
1758 }
1759
1760 fn postfix_inc_dec_suggest(
1761 &mut self,
1762 base_src: String,
1763 kind: IncDecRecovery,
1764 (pre_span, post_span): (Span, Span),
1765 ) -> MultiSugg {
1766 let tmp_var = if base_src.trim() == "tmp" { "tmp_" } else { "tmp" };
1767 MultiSugg {
1768 msg: format!("use `{}= 1` instead", kind.op.chr()),
1769 patches: vec![
1770 (pre_span, format!("{{ let {tmp_var} = ")),
1771 (post_span, format!("; {} {}= 1; {} }}", base_src, kind.op.chr(), tmp_var)),
1772 ],
1773 applicability: Applicability::HasPlaceholders,
1774 }
1775 }
1776
1777 fn inc_dec_standalone_suggest(
1778 &mut self,
1779 kind: IncDecRecovery,
1780 (pre_span, post_span): (Span, Span),
1781 ) -> MultiSugg {
1782 let mut patches = Vec::new();
1783
1784 if !pre_span.is_empty() {
1785 patches.push((pre_span, String::new()));
1786 }
1787
1788 patches.push((post_span, format!(" {}= 1", kind.op.chr())));
1789 MultiSugg {
1790 msg: format!("use `{}= 1` instead", kind.op.chr()),
1791 patches,
1792 applicability: Applicability::MachineApplicable,
1793 }
1794 }
1795
1796 pub(super) fn maybe_recover_from_bad_qpath<T: RecoverQPath>(
1800 &mut self,
1801 base: P<T>,
1802 ) -> PResult<'a, P<T>> {
1803 if !self.may_recover() {
1804 return Ok(base);
1805 }
1806
1807 if self.token == token::PathSep {
1809 if let Some(ty) = base.to_ty() {
1810 return self.maybe_recover_from_bad_qpath_stage_2(ty.span, ty);
1811 }
1812 }
1813 Ok(base)
1814 }
1815
1816 pub(super) fn maybe_recover_from_bad_qpath_stage_2<T: RecoverQPath>(
1819 &mut self,
1820 ty_span: Span,
1821 ty: P<Ty>,
1822 ) -> PResult<'a, P<T>> {
1823 self.expect(exp!(PathSep))?;
1824
1825 let mut path = ast::Path { segments: ThinVec::new(), span: DUMMY_SP, tokens: None };
1826 self.parse_path_segments(&mut path.segments, T::PATH_STYLE, None)?;
1827 path.span = ty_span.to(self.prev_token.span);
1828
1829 self.dcx().emit_err(BadQPathStage2 {
1830 span: ty_span,
1831 wrap: WrapType { lo: ty_span.shrink_to_lo(), hi: ty_span.shrink_to_hi() },
1832 });
1833
1834 let path_span = ty_span.shrink_to_hi(); Ok(P(T::recovered(Some(P(QSelf { ty, path_span, position: 0 })), path)))
1836 }
1837
1838 pub fn maybe_consume_incorrect_semicolon(&mut self, previous_item: Option<&Item>) -> bool {
1841 if self.token != TokenKind::Semi {
1842 return false;
1843 }
1844
1845 let err = match previous_item {
1848 Some(previous_item) => {
1849 let name = match previous_item.kind {
1850 ItemKind::Struct(..) => "braced struct",
1853 _ => previous_item.kind.descr(),
1854 };
1855 IncorrectSemicolon { span: self.token.span, name, show_help: true }
1856 }
1857 None => IncorrectSemicolon { span: self.token.span, name: "", show_help: false },
1858 };
1859 self.dcx().emit_err(err);
1860
1861 self.bump();
1862 true
1863 }
1864
1865 pub(super) fn unexpected_try_recover(&mut self, t: &TokenKind) -> PResult<'a, Recovered> {
1868 let token_str = pprust::token_kind_to_string(t);
1869 let this_token_str = super::token_descr(&self.token);
1870 let (prev_sp, sp) = match (&self.token.kind, self.subparser_name) {
1871 (token::Eof, Some(_)) => {
1873 let sp = self.prev_token.span.shrink_to_hi();
1874 (sp, sp)
1875 }
1876 _ if self.prev_token.span == DUMMY_SP => (self.token.span, self.token.span),
1879 (token::Eof, None) => (self.prev_token.span, self.token.span),
1881 _ => (self.prev_token.span.shrink_to_hi(), self.token.span),
1882 };
1883 let msg = format!(
1884 "expected `{}`, found {}",
1885 token_str,
1886 match (&self.token.kind, self.subparser_name) {
1887 (token::Eof, Some(origin)) => format!("end of {origin}"),
1888 _ => this_token_str,
1889 },
1890 );
1891 let mut err = self.dcx().struct_span_err(sp, msg);
1892 let label_exp = format!("expected `{token_str}`");
1893 let sm = self.psess.source_map();
1894 if !sm.is_multiline(prev_sp.until(sp)) {
1895 err.span_label(sp, label_exp);
1898 } else {
1899 err.span_label(prev_sp, label_exp);
1900 err.span_label(sp, "unexpected token");
1901 }
1902 Err(err)
1903 }
1904
1905 pub(super) fn expect_semi(&mut self) -> PResult<'a, ()> {
1906 if self.eat(exp!(Semi)) || self.recover_colon_as_semi() {
1907 return Ok(());
1908 }
1909 self.expect(exp!(Semi)).map(drop) }
1911
1912 pub(super) fn recover_colon_as_semi(&mut self) -> bool {
1913 let line_idx = |span: Span| {
1914 self.psess
1915 .source_map()
1916 .span_to_lines(span)
1917 .ok()
1918 .and_then(|lines| Some(lines.lines.get(0)?.line_index))
1919 };
1920
1921 if self.may_recover()
1922 && self.token == token::Colon
1923 && self.look_ahead(1, |next| line_idx(self.token.span) < line_idx(next.span))
1924 {
1925 self.dcx().emit_err(ColonAsSemi {
1926 span: self.token.span,
1927 type_ascription: self.psess.unstable_features.is_nightly_build(),
1928 });
1929 self.bump();
1930 return true;
1931 }
1932
1933 false
1934 }
1935
1936 pub(super) fn recover_incorrect_await_syntax(
1939 &mut self,
1940 await_sp: Span,
1941 ) -> PResult<'a, P<Expr>> {
1942 let (hi, expr, is_question) = if self.token == token::Bang {
1943 self.recover_await_macro()?
1945 } else {
1946 self.recover_await_prefix(await_sp)?
1947 };
1948 let (sp, guar) = self.error_on_incorrect_await(await_sp, hi, &expr, is_question);
1949 let expr = self.mk_expr_err(await_sp.to(sp), guar);
1950 self.maybe_recover_from_bad_qpath(expr)
1951 }
1952
1953 fn recover_await_macro(&mut self) -> PResult<'a, (Span, P<Expr>, bool)> {
1954 self.expect(exp!(Bang))?;
1955 self.expect(exp!(OpenParen))?;
1956 let expr = self.parse_expr()?;
1957 self.expect(exp!(CloseParen))?;
1958 Ok((self.prev_token.span, expr, false))
1959 }
1960
1961 fn recover_await_prefix(&mut self, await_sp: Span) -> PResult<'a, (Span, P<Expr>, bool)> {
1962 let is_question = self.eat(exp!(Question)); let expr = if self.token == token::OpenDelim(Delimiter::Brace) {
1964 self.parse_expr_block(None, self.token.span, BlockCheckMode::Default)
1968 } else {
1969 self.parse_expr()
1970 }
1971 .map_err(|mut err| {
1972 err.span_label(await_sp, format!("while parsing this incorrect await expression"));
1973 err
1974 })?;
1975 Ok((expr.span, expr, is_question))
1976 }
1977
1978 fn error_on_incorrect_await(
1979 &self,
1980 lo: Span,
1981 hi: Span,
1982 expr: &Expr,
1983 is_question: bool,
1984 ) -> (Span, ErrorGuaranteed) {
1985 let span = lo.to(hi);
1986 let guar = self.dcx().emit_err(IncorrectAwait {
1987 span,
1988 suggestion: AwaitSuggestion {
1989 removal: lo.until(expr.span),
1990 dot_await: expr.span.shrink_to_hi(),
1991 question_mark: if is_question { "?" } else { "" },
1992 },
1993 });
1994 (span, guar)
1995 }
1996
1997 pub(super) fn recover_from_await_method_call(&mut self) {
1999 if self.token == token::OpenDelim(Delimiter::Parenthesis)
2000 && self.look_ahead(1, |t| t == &token::CloseDelim(Delimiter::Parenthesis))
2001 {
2002 let lo = self.token.span;
2004 self.bump(); let span = lo.to(self.token.span);
2006 self.bump(); self.dcx().emit_err(IncorrectUseOfAwait { span });
2009 }
2010 }
2011 pub(super) fn recover_from_use(&mut self) {
2014 if self.token == token::OpenDelim(Delimiter::Parenthesis)
2015 && self.look_ahead(1, |t| t == &token::CloseDelim(Delimiter::Parenthesis))
2016 {
2017 let lo = self.token.span;
2019 self.bump(); let span = lo.to(self.token.span);
2021 self.bump(); self.dcx().emit_err(IncorrectUseOfUse { span });
2024 }
2025 }
2026
2027 pub(super) fn try_macro_suggestion(&mut self) -> PResult<'a, P<Expr>> {
2028 let is_try = self.token.is_keyword(kw::Try);
2029 let is_questionmark = self.look_ahead(1, |t| t == &token::Bang); let is_open = self.look_ahead(2, |t| t == &token::OpenDelim(Delimiter::Parenthesis)); if is_try && is_questionmark && is_open {
2033 let lo = self.token.span;
2034 self.bump(); self.bump(); let try_span = lo.to(self.token.span); self.bump(); let is_empty = self.token == token::CloseDelim(Delimiter::Parenthesis); self.consume_block(exp!(OpenParen), exp!(CloseParen), ConsumeClosingDelim::No); let hi = self.token.span;
2041 self.bump(); let mut err = self.dcx().struct_span_err(lo.to(hi), "use of deprecated `try` macro");
2043 err.note("in the 2018 edition `try` is a reserved keyword, and the `try!()` macro is deprecated");
2044 let prefix = if is_empty { "" } else { "alternatively, " };
2045 if !is_empty {
2046 err.multipart_suggestion(
2047 "you can use the `?` operator instead",
2048 vec![(try_span, "".to_owned()), (hi, "?".to_owned())],
2049 Applicability::MachineApplicable,
2050 );
2051 }
2052 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);
2053 let guar = err.emit();
2054 Ok(self.mk_expr_err(lo.to(hi), guar))
2055 } else {
2056 Err(self.expected_expression_found()) }
2058 }
2059
2060 pub(super) fn expect_gt_or_maybe_suggest_closing_generics(
2067 &mut self,
2068 params: &[ast::GenericParam],
2069 ) -> PResult<'a, ()> {
2070 let Err(mut err) = self.expect_gt() else {
2071 return Ok(());
2072 };
2073 if let [.., ast::GenericParam { bounds, .. }] = params
2075 && let Some(poly) = bounds
2076 .iter()
2077 .filter_map(|bound| match bound {
2078 ast::GenericBound::Trait(poly) => Some(poly),
2079 _ => None,
2080 })
2081 .next_back()
2082 {
2083 err.span_suggestion_verbose(
2084 poly.span.shrink_to_hi(),
2085 "you might have meant to end the type parameters here",
2086 ">",
2087 Applicability::MaybeIncorrect,
2088 );
2089 }
2090 Err(err)
2091 }
2092
2093 pub(super) fn recover_seq_parse_error(
2094 &mut self,
2095 open: ExpTokenPair<'_>,
2096 close: ExpTokenPair<'_>,
2097 lo: Span,
2098 err: Diag<'a>,
2099 ) -> P<Expr> {
2100 let guar = err.emit();
2101 self.consume_block(open, close, ConsumeClosingDelim::Yes);
2103 self.mk_expr(lo.to(self.prev_token.span), ExprKind::Err(guar))
2104 }
2105
2106 pub(super) fn recover_stmt(&mut self) {
2111 self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore)
2112 }
2113
2114 pub(super) fn recover_stmt_(
2122 &mut self,
2123 break_on_semi: SemiColonMode,
2124 break_on_block: BlockMode,
2125 ) {
2126 let mut brace_depth = 0;
2127 let mut bracket_depth = 0;
2128 let mut in_block = false;
2129 debug!("recover_stmt_ enter loop (semi={:?}, block={:?})", break_on_semi, break_on_block);
2130 loop {
2131 debug!("recover_stmt_ loop {:?}", self.token);
2132 match self.token.kind {
2133 token::OpenDelim(Delimiter::Brace) => {
2134 brace_depth += 1;
2135 self.bump();
2136 if break_on_block == BlockMode::Break && brace_depth == 1 && bracket_depth == 0
2137 {
2138 in_block = true;
2139 }
2140 }
2141 token::OpenDelim(Delimiter::Bracket) => {
2142 bracket_depth += 1;
2143 self.bump();
2144 }
2145 token::CloseDelim(Delimiter::Brace) => {
2146 if brace_depth == 0 {
2147 debug!("recover_stmt_ return - close delim {:?}", self.token);
2148 break;
2149 }
2150 brace_depth -= 1;
2151 self.bump();
2152 if in_block && bracket_depth == 0 && brace_depth == 0 {
2153 debug!("recover_stmt_ return - block end {:?}", self.token);
2154 break;
2155 }
2156 }
2157 token::CloseDelim(Delimiter::Bracket) => {
2158 bracket_depth -= 1;
2159 if bracket_depth < 0 {
2160 bracket_depth = 0;
2161 }
2162 self.bump();
2163 }
2164 token::Eof => {
2165 debug!("recover_stmt_ return - Eof");
2166 break;
2167 }
2168 token::Semi => {
2169 self.bump();
2170 if break_on_semi == SemiColonMode::Break
2171 && brace_depth == 0
2172 && bracket_depth == 0
2173 {
2174 debug!("recover_stmt_ return - Semi");
2175 break;
2176 }
2177 }
2178 token::Comma
2179 if break_on_semi == SemiColonMode::Comma
2180 && brace_depth == 0
2181 && bracket_depth == 0 =>
2182 {
2183 break;
2184 }
2185 _ => self.bump(),
2186 }
2187 }
2188 }
2189
2190 pub(super) fn check_for_for_in_in_typo(&mut self, in_span: Span) {
2191 if self.eat_keyword(exp!(In)) {
2192 self.dcx().emit_err(InInTypo {
2194 span: self.prev_token.span,
2195 sugg_span: in_span.until(self.prev_token.span),
2196 });
2197 }
2198 }
2199
2200 pub(super) fn eat_incorrect_doc_comment_for_param_type(&mut self) {
2201 if let token::DocComment(..) = self.token.kind {
2202 self.dcx().emit_err(DocCommentOnParamType { span: self.token.span });
2203 self.bump();
2204 } else if self.token == token::Pound
2205 && self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Bracket))
2206 {
2207 let lo = self.token.span;
2208 while self.token != token::CloseDelim(Delimiter::Bracket) {
2210 self.bump();
2211 }
2212 let sp = lo.to(self.token.span);
2213 self.bump();
2214 self.dcx().emit_err(AttributeOnParamType { span: sp });
2215 }
2216 }
2217
2218 pub(super) fn parameter_without_type(
2219 &mut self,
2220 err: &mut Diag<'_>,
2221 pat: P<ast::Pat>,
2222 require_name: bool,
2223 first_param: bool,
2224 ) -> Option<Ident> {
2225 if self.check_ident()
2228 && self.look_ahead(1, |t| {
2229 *t == token::Comma || *t == token::CloseDelim(Delimiter::Parenthesis)
2230 })
2231 {
2232 let ident = self.parse_ident().unwrap();
2234 let span = pat.span.with_hi(ident.span.hi());
2235
2236 err.span_suggestion(
2237 span,
2238 "declare the type after the parameter binding",
2239 "<identifier>: <type>",
2240 Applicability::HasPlaceholders,
2241 );
2242 return Some(ident);
2243 } else if require_name
2244 && (self.token == token::Comma
2245 || self.token == token::Lt
2246 || self.token == token::CloseDelim(Delimiter::Parenthesis))
2247 {
2248 let rfc_note = "anonymous parameters are removed in the 2018 edition (see RFC 1685)";
2249
2250 let (ident, self_sugg, param_sugg, type_sugg, self_span, param_span, type_span) =
2251 match pat.kind {
2252 PatKind::Ident(_, ident, _) => (
2253 ident,
2254 "self: ",
2255 ": TypeName".to_string(),
2256 "_: ",
2257 pat.span.shrink_to_lo(),
2258 pat.span.shrink_to_hi(),
2259 pat.span.shrink_to_lo(),
2260 ),
2261 PatKind::Ref(ref inner_pat, mutab)
2263 if matches!(inner_pat.clone().into_inner().kind, PatKind::Ident(..)) =>
2264 {
2265 match inner_pat.clone().into_inner().kind {
2266 PatKind::Ident(_, ident, _) => {
2267 let mutab = mutab.prefix_str();
2268 (
2269 ident,
2270 "self: ",
2271 format!("{ident}: &{mutab}TypeName"),
2272 "_: ",
2273 pat.span.shrink_to_lo(),
2274 pat.span,
2275 pat.span.shrink_to_lo(),
2276 )
2277 }
2278 _ => unreachable!(),
2279 }
2280 }
2281 _ => {
2282 if let Some(_) = pat.to_ty() {
2284 err.span_suggestion_verbose(
2285 pat.span.shrink_to_lo(),
2286 "explicitly ignore the parameter name",
2287 "_: ".to_string(),
2288 Applicability::MachineApplicable,
2289 );
2290 err.note(rfc_note);
2291 }
2292
2293 return None;
2294 }
2295 };
2296
2297 if first_param {
2299 err.span_suggestion_verbose(
2300 self_span,
2301 "if this is a `self` type, give it a parameter name",
2302 self_sugg,
2303 Applicability::MaybeIncorrect,
2304 );
2305 }
2306 if self.token != token::Lt {
2309 err.span_suggestion_verbose(
2310 param_span,
2311 "if this is a parameter name, give it a type",
2312 param_sugg,
2313 Applicability::HasPlaceholders,
2314 );
2315 }
2316 err.span_suggestion_verbose(
2317 type_span,
2318 "if this is a type, explicitly ignore the parameter name",
2319 type_sugg,
2320 Applicability::MachineApplicable,
2321 );
2322 err.note(rfc_note);
2323
2324 return if self.token == token::Lt { None } else { Some(ident) };
2326 }
2327 None
2328 }
2329
2330 pub(super) fn recover_arg_parse(&mut self) -> PResult<'a, (P<ast::Pat>, P<ast::Ty>)> {
2331 let pat = self.parse_pat_no_top_alt(Some(Expected::ArgumentName), None)?;
2332 self.expect(exp!(Colon))?;
2333 let ty = self.parse_ty()?;
2334
2335 self.dcx().emit_err(PatternMethodParamWithoutBody { span: pat.span });
2336
2337 let pat =
2339 P(Pat { kind: PatKind::Wild, span: pat.span, id: ast::DUMMY_NODE_ID, tokens: None });
2340 Ok((pat, ty))
2341 }
2342
2343 pub(super) fn recover_bad_self_param(&mut self, mut param: Param) -> PResult<'a, Param> {
2344 let span = param.pat.span;
2345 let guar = self.dcx().emit_err(SelfParamNotFirst { span });
2346 param.ty.kind = TyKind::Err(guar);
2347 Ok(param)
2348 }
2349
2350 pub(super) fn consume_block(
2351 &mut self,
2352 open: ExpTokenPair<'_>,
2353 close: ExpTokenPair<'_>,
2354 consume_close: ConsumeClosingDelim,
2355 ) {
2356 let mut brace_depth = 0;
2357 loop {
2358 if self.eat(open) {
2359 brace_depth += 1;
2360 } else if self.check(close) {
2361 if brace_depth == 0 {
2362 if let ConsumeClosingDelim::Yes = consume_close {
2363 self.bump();
2367 }
2368 return;
2369 } else {
2370 self.bump();
2371 brace_depth -= 1;
2372 continue;
2373 }
2374 } else if self.token == token::Eof {
2375 return;
2376 } else {
2377 self.bump();
2378 }
2379 }
2380 }
2381
2382 pub(super) fn expected_expression_found(&self) -> Diag<'a> {
2383 let (span, msg) = match (&self.token.kind, self.subparser_name) {
2384 (&token::Eof, Some(origin)) => {
2385 let sp = self.prev_token.span.shrink_to_hi();
2386 (sp, format!("expected expression, found end of {origin}"))
2387 }
2388 _ => (
2389 self.token.span,
2390 format!("expected expression, found {}", super::token_descr(&self.token)),
2391 ),
2392 };
2393 let mut err = self.dcx().struct_span_err(span, msg);
2394 let sp = self.psess.source_map().start_point(self.token.span);
2395 if let Some(sp) = self.psess.ambiguous_block_expr_parse.borrow().get(&sp) {
2396 err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
2397 }
2398 err.span_label(span, "expected expression");
2399 err
2400 }
2401
2402 fn consume_tts(
2403 &mut self,
2404 mut acc: i64, modifier: &[(token::TokenKind, i64)],
2407 ) {
2408 while acc > 0 {
2409 if let Some((_, val)) = modifier.iter().find(|(t, _)| self.token == *t) {
2410 acc += *val;
2411 }
2412 if self.token == token::Eof {
2413 break;
2414 }
2415 self.bump();
2416 }
2417 }
2418
2419 pub(super) fn deduplicate_recovered_params_names(&self, fn_inputs: &mut ThinVec<Param>) {
2428 let mut seen_inputs = FxHashSet::default();
2429 for input in fn_inputs.iter_mut() {
2430 let opt_ident = if let (PatKind::Ident(_, ident, _), TyKind::Err(_)) =
2431 (&input.pat.kind, &input.ty.kind)
2432 {
2433 Some(*ident)
2434 } else {
2435 None
2436 };
2437 if let Some(ident) = opt_ident {
2438 if seen_inputs.contains(&ident) {
2439 input.pat.kind = PatKind::Wild;
2440 }
2441 seen_inputs.insert(ident);
2442 }
2443 }
2444 }
2445
2446 pub(super) fn handle_ambiguous_unbraced_const_arg(
2450 &mut self,
2451 args: &mut ThinVec<AngleBracketedArg>,
2452 ) -> PResult<'a, bool> {
2453 let arg = args.pop().unwrap();
2457 let mut err = self.dcx().struct_span_err(
2463 self.token.span,
2464 format!("expected one of `,` or `>`, found {}", super::token_descr(&self.token)),
2465 );
2466 err.span_label(self.token.span, "expected one of `,` or `>`");
2467 match self.recover_const_arg(arg.span(), err) {
2468 Ok(arg) => {
2469 args.push(AngleBracketedArg::Arg(arg));
2470 if self.eat(exp!(Comma)) {
2471 return Ok(true); }
2473 }
2474 Err(err) => {
2475 args.push(arg);
2476 err.delay_as_bug();
2478 }
2479 }
2480 Ok(false) }
2482
2483 pub(super) fn handle_unambiguous_unbraced_const_arg(&mut self) -> PResult<'a, P<Expr>> {
2491 let start = self.token.span;
2492 let attrs = self.parse_outer_attributes()?;
2493 let (expr, _) =
2494 self.parse_expr_res(Restrictions::CONST_EXPR, attrs).map_err(|mut err| {
2495 err.span_label(
2496 start.shrink_to_lo(),
2497 "while parsing a const generic argument starting here",
2498 );
2499 err
2500 })?;
2501 if !self.expr_is_valid_const_arg(&expr) {
2502 self.dcx().emit_err(ConstGenericWithoutBraces {
2503 span: expr.span,
2504 sugg: ConstGenericWithoutBracesSugg {
2505 left: expr.span.shrink_to_lo(),
2506 right: expr.span.shrink_to_hi(),
2507 },
2508 });
2509 }
2510 Ok(expr)
2511 }
2512
2513 fn recover_const_param_decl(&mut self, ty_generics: Option<&Generics>) -> Option<GenericArg> {
2514 let snapshot = self.create_snapshot_for_diagnostic();
2515 let param = match self.parse_const_param(AttrVec::new()) {
2516 Ok(param) => param,
2517 Err(err) => {
2518 err.cancel();
2519 self.restore_snapshot(snapshot);
2520 return None;
2521 }
2522 };
2523
2524 let ident = param.ident.to_string();
2525 let sugg = match (ty_generics, self.psess.source_map().span_to_snippet(param.span())) {
2526 (Some(Generics { params, span: impl_generics, .. }), Ok(snippet)) => {
2527 Some(match ¶ms[..] {
2528 [] => UnexpectedConstParamDeclarationSugg::AddParam {
2529 impl_generics: *impl_generics,
2530 incorrect_decl: param.span(),
2531 snippet,
2532 ident,
2533 },
2534 [.., generic] => UnexpectedConstParamDeclarationSugg::AppendParam {
2535 impl_generics_end: generic.span().shrink_to_hi(),
2536 incorrect_decl: param.span(),
2537 snippet,
2538 ident,
2539 },
2540 })
2541 }
2542 _ => None,
2543 };
2544 let guar =
2545 self.dcx().emit_err(UnexpectedConstParamDeclaration { span: param.span(), sugg });
2546
2547 let value = self.mk_expr_err(param.span(), guar);
2548 Some(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value }))
2549 }
2550
2551 pub(super) fn recover_const_param_declaration(
2552 &mut self,
2553 ty_generics: Option<&Generics>,
2554 ) -> PResult<'a, Option<GenericArg>> {
2555 if let Some(arg) = self.recover_const_param_decl(ty_generics) {
2557 return Ok(Some(arg));
2558 }
2559
2560 let start = self.token.span;
2562 self.bump(); let mut err = UnexpectedConstInGenericParam { span: start, to_remove: None };
2566 if self.check_const_arg() {
2567 err.to_remove = Some(start.until(self.token.span));
2568 self.dcx().emit_err(err);
2569 Ok(Some(GenericArg::Const(self.parse_const_arg()?)))
2570 } else {
2571 let after_kw_const = self.token.span;
2572 self.recover_const_arg(after_kw_const, self.dcx().create_err(err)).map(Some)
2573 }
2574 }
2575
2576 pub(super) fn recover_const_arg(
2582 &mut self,
2583 start: Span,
2584 mut err: Diag<'a>,
2585 ) -> PResult<'a, GenericArg> {
2586 let is_op_or_dot = AssocOp::from_token(&self.token)
2587 .and_then(|op| {
2588 if let AssocOp::Binary(
2589 BinOpKind::Gt
2590 | BinOpKind::Lt
2591 | BinOpKind::Shr
2592 | BinOpKind::Ge
2593 )
2594 | AssocOp::Assign
2597 | AssocOp::AssignOp(_) = op
2598 {
2599 None
2600 } else {
2601 Some(op)
2602 }
2603 })
2604 .is_some()
2605 || self.token == TokenKind::Dot;
2606 let was_op = matches!(self.prev_token.kind, token::Plus | token::Shr | token::Gt);
2609 if !is_op_or_dot && !was_op {
2610 return Err(err);
2612 }
2613 let snapshot = self.create_snapshot_for_diagnostic();
2614 if is_op_or_dot {
2615 self.bump();
2616 }
2617 match (|| {
2618 let attrs = self.parse_outer_attributes()?;
2619 self.parse_expr_res(Restrictions::CONST_EXPR, attrs)
2620 })() {
2621 Ok((expr, _)) => {
2622 if snapshot.token == token::EqEq {
2624 err.span_suggestion(
2625 snapshot.token.span,
2626 "if you meant to use an associated type binding, replace `==` with `=`",
2627 "=",
2628 Applicability::MaybeIncorrect,
2629 );
2630 let guar = err.emit();
2631 let value = self.mk_expr_err(start.to(expr.span), guar);
2632 return Ok(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value }));
2633 } else if snapshot.token == token::Colon
2634 && expr.span.lo() == snapshot.token.span.hi()
2635 && matches!(expr.kind, ExprKind::Path(..))
2636 {
2637 err.span_suggestion(
2639 snapshot.token.span,
2640 "write a path separator here",
2641 "::",
2642 Applicability::MaybeIncorrect,
2643 );
2644 let guar = err.emit();
2645 return Ok(GenericArg::Type(
2646 self.mk_ty(start.to(expr.span), TyKind::Err(guar)),
2647 ));
2648 } else if self.token == token::Comma || self.token.kind.should_end_const_arg() {
2649 return Ok(self.dummy_const_arg_needs_braces(err, start.to(expr.span)));
2656 }
2657 }
2658 Err(err) => {
2659 err.cancel();
2660 }
2661 }
2662 self.restore_snapshot(snapshot);
2663 Err(err)
2664 }
2665
2666 pub(crate) fn recover_unbraced_const_arg_that_can_begin_ty(
2670 &mut self,
2671 mut snapshot: SnapshotParser<'a>,
2672 ) -> Option<P<ast::Expr>> {
2673 match (|| {
2674 let attrs = self.parse_outer_attributes()?;
2675 snapshot.parse_expr_res(Restrictions::CONST_EXPR, attrs)
2676 })() {
2677 Ok((expr, _)) if let token::Comma | token::Gt = snapshot.token.kind => {
2680 self.restore_snapshot(snapshot);
2681 Some(expr)
2682 }
2683 Ok(_) => None,
2684 Err(err) => {
2685 err.cancel();
2686 None
2687 }
2688 }
2689 }
2690
2691 pub(super) fn dummy_const_arg_needs_braces(&self, mut err: Diag<'a>, span: Span) -> GenericArg {
2693 err.multipart_suggestion(
2694 "expressions must be enclosed in braces to be used as const generic \
2695 arguments",
2696 vec![(span.shrink_to_lo(), "{ ".to_string()), (span.shrink_to_hi(), " }".to_string())],
2697 Applicability::MaybeIncorrect,
2698 );
2699 let guar = err.emit();
2700 let value = self.mk_expr_err(span, guar);
2701 GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value })
2702 }
2703
2704 pub(crate) fn maybe_recover_colon_colon_in_pat_typo(
2707 &mut self,
2708 mut first_pat: P<Pat>,
2709 expected: Option<Expected>,
2710 ) -> P<Pat> {
2711 if token::Colon != self.token.kind {
2712 return first_pat;
2713 }
2714 if !matches!(first_pat.kind, PatKind::Ident(_, _, None) | PatKind::Path(..))
2715 || !self.look_ahead(1, |token| token.is_ident() && !token.is_reserved_ident())
2716 {
2717 let mut snapshot_type = self.create_snapshot_for_diagnostic();
2718 snapshot_type.bump(); match snapshot_type.parse_ty() {
2720 Err(inner_err) => {
2721 inner_err.cancel();
2722 }
2723 Ok(ty) => {
2724 let Err(mut err) = self.expected_one_of_not_found(&[], &[]) else {
2725 return first_pat;
2726 };
2727 err.span_label(ty.span, "specifying the type of a pattern isn't supported");
2728 self.restore_snapshot(snapshot_type);
2729 let span = first_pat.span.to(ty.span);
2730 first_pat = self.mk_pat(span, PatKind::Wild);
2731 err.emit();
2732 }
2733 }
2734 return first_pat;
2735 }
2736 let colon_span = self.token.span;
2739 let mut snapshot_pat = self.create_snapshot_for_diagnostic();
2742 let mut snapshot_type = self.create_snapshot_for_diagnostic();
2743
2744 match self.expected_one_of_not_found(&[], &[]) {
2746 Err(mut err) => {
2747 snapshot_pat.bump();
2749 snapshot_type.bump();
2750 match snapshot_pat.parse_pat_no_top_alt(expected, None) {
2751 Err(inner_err) => {
2752 inner_err.cancel();
2753 }
2754 Ok(mut pat) => {
2755 let new_span = first_pat.span.to(pat.span);
2757 let mut show_sugg = false;
2758 match &mut pat.kind {
2760 PatKind::Struct(qself @ None, path, ..)
2761 | PatKind::TupleStruct(qself @ None, path, _)
2762 | PatKind::Path(qself @ None, path) => match &first_pat.kind {
2763 PatKind::Ident(_, ident, _) => {
2764 path.segments.insert(0, PathSegment::from_ident(*ident));
2765 path.span = new_span;
2766 show_sugg = true;
2767 first_pat = pat;
2768 }
2769 PatKind::Path(old_qself, old_path) => {
2770 path.segments = old_path
2771 .segments
2772 .iter()
2773 .cloned()
2774 .chain(take(&mut path.segments))
2775 .collect();
2776 path.span = new_span;
2777 *qself = old_qself.clone();
2778 first_pat = pat;
2779 show_sugg = true;
2780 }
2781 _ => {}
2782 },
2783 PatKind::Ident(BindingMode::NONE, ident, None) => {
2784 match &first_pat.kind {
2785 PatKind::Ident(_, old_ident, _) => {
2786 let path = PatKind::Path(
2787 None,
2788 Path {
2789 span: new_span,
2790 segments: thin_vec![
2791 PathSegment::from_ident(*old_ident),
2792 PathSegment::from_ident(*ident),
2793 ],
2794 tokens: None,
2795 },
2796 );
2797 first_pat = self.mk_pat(new_span, path);
2798 show_sugg = true;
2799 }
2800 PatKind::Path(old_qself, old_path) => {
2801 let mut segments = old_path.segments.clone();
2802 segments.push(PathSegment::from_ident(*ident));
2803 let path = PatKind::Path(
2804 old_qself.clone(),
2805 Path { span: new_span, segments, tokens: None },
2806 );
2807 first_pat = self.mk_pat(new_span, path);
2808 show_sugg = true;
2809 }
2810 _ => {}
2811 }
2812 }
2813 _ => {}
2814 }
2815 if show_sugg {
2816 err.span_suggestion_verbose(
2817 colon_span.until(self.look_ahead(1, |t| t.span)),
2818 "maybe write a path separator here",
2819 "::",
2820 Applicability::MaybeIncorrect,
2821 );
2822 } else {
2823 first_pat = self.mk_pat(new_span, PatKind::Wild);
2824 }
2825 self.restore_snapshot(snapshot_pat);
2826 }
2827 }
2828 match snapshot_type.parse_ty() {
2829 Err(inner_err) => {
2830 inner_err.cancel();
2831 }
2832 Ok(ty) => {
2833 err.span_label(ty.span, "specifying the type of a pattern isn't supported");
2834 self.restore_snapshot(snapshot_type);
2835 let new_span = first_pat.span.to(ty.span);
2836 first_pat = self.mk_pat(new_span, PatKind::Wild);
2837 }
2838 }
2839 err.emit();
2840 }
2841 _ => {
2842 }
2844 };
2845 first_pat
2846 }
2847
2848 pub(crate) fn maybe_recover_unexpected_block_label(
2851 &mut self,
2852 loop_header: Option<Span>,
2853 ) -> bool {
2854 if !(self.check_lifetime()
2856 && self.look_ahead(1, |t| *t == token::Colon)
2857 && self.look_ahead(2, |t| *t == token::OpenDelim(Delimiter::Brace)))
2858 {
2859 return false;
2860 }
2861 let label = self.eat_label().expect("just checked if a label exists");
2862 self.bump(); let span = label.ident.span.to(self.prev_token.span);
2864 let mut diag = self
2865 .dcx()
2866 .struct_span_err(span, "block label not supported here")
2867 .with_span_label(span, "not supported here");
2868 if let Some(loop_header) = loop_header {
2869 diag.multipart_suggestion(
2870 "if you meant to label the loop, move this label before the loop",
2871 vec![
2872 (label.ident.span.until(self.token.span), String::from("")),
2873 (loop_header.shrink_to_lo(), format!("{}: ", label.ident)),
2874 ],
2875 Applicability::MachineApplicable,
2876 );
2877 } else {
2878 diag.tool_only_span_suggestion(
2879 label.ident.span.until(self.token.span),
2880 "remove this block label",
2881 "",
2882 Applicability::MachineApplicable,
2883 );
2884 }
2885 diag.emit();
2886 true
2887 }
2888
2889 pub(crate) fn maybe_recover_unexpected_comma(
2892 &mut self,
2893 lo: Span,
2894 rt: CommaRecoveryMode,
2895 ) -> PResult<'a, ()> {
2896 if self.token != token::Comma {
2897 return Ok(());
2898 }
2899
2900 let comma_span = self.token.span;
2905 self.bump();
2906 if let Err(err) = self.skip_pat_list() {
2907 err.cancel();
2910 }
2911 let seq_span = lo.to(self.prev_token.span);
2912 let mut err = self.dcx().struct_span_err(comma_span, "unexpected `,` in pattern");
2913 if let Ok(seq_snippet) = self.span_to_snippet(seq_span) {
2914 err.multipart_suggestion(
2915 format!(
2916 "try adding parentheses to match on a tuple{}",
2917 if let CommaRecoveryMode::LikelyTuple = rt { "" } else { "..." },
2918 ),
2919 vec![
2920 (seq_span.shrink_to_lo(), "(".to_string()),
2921 (seq_span.shrink_to_hi(), ")".to_string()),
2922 ],
2923 Applicability::MachineApplicable,
2924 );
2925 if let CommaRecoveryMode::EitherTupleOrPipe = rt {
2926 err.span_suggestion(
2927 seq_span,
2928 "...or a vertical bar to match on multiple alternatives",
2929 seq_snippet.replace(',', " |"),
2930 Applicability::MachineApplicable,
2931 );
2932 }
2933 }
2934 Err(err)
2935 }
2936
2937 pub(crate) fn maybe_recover_bounds_doubled_colon(&mut self, ty: &Ty) -> PResult<'a, ()> {
2938 let TyKind::Path(qself, path) = &ty.kind else { return Ok(()) };
2939 let qself_position = qself.as_ref().map(|qself| qself.position);
2940 for (i, segments) in path.segments.windows(2).enumerate() {
2941 if qself_position.is_some_and(|pos| i < pos) {
2942 continue;
2943 }
2944 if let [a, b] = segments {
2945 let (a_span, b_span) = (a.span(), b.span());
2946 let between_span = a_span.shrink_to_hi().to(b_span.shrink_to_lo());
2947 if self.span_to_snippet(between_span).as_deref() == Ok(":: ") {
2948 return Err(self.dcx().create_err(DoubleColonInBound {
2949 span: path.span.shrink_to_hi(),
2950 between: between_span,
2951 }));
2952 }
2953 }
2954 }
2955 Ok(())
2956 }
2957
2958 pub(crate) fn maybe_err_dotdotlt_syntax(&self, maybe_lt: Token, mut err: Diag<'a>) -> Diag<'a> {
2960 if maybe_lt == token::Lt
2961 && (self.expected_token_types.contains(TokenType::Gt)
2962 || matches!(self.token.kind, token::Literal(..)))
2963 {
2964 err.span_suggestion(
2965 maybe_lt.span,
2966 "remove the `<` to write an exclusive range",
2967 "",
2968 Applicability::MachineApplicable,
2969 );
2970 }
2971 err
2972 }
2973
2974 pub(super) fn is_vcs_conflict_marker(
2982 &mut self,
2983 long_kind: &TokenKind,
2984 short_kind: &TokenKind,
2985 ) -> bool {
2986 (0..3).all(|i| self.look_ahead(i, |tok| tok == long_kind))
2987 && self.look_ahead(3, |tok| tok == short_kind)
2988 }
2989
2990 fn conflict_marker(&mut self, long_kind: &TokenKind, short_kind: &TokenKind) -> Option<Span> {
2991 if self.is_vcs_conflict_marker(long_kind, short_kind) {
2992 let lo = self.token.span;
2993 for _ in 0..4 {
2994 self.bump();
2995 }
2996 return Some(lo.to(self.prev_token.span));
2997 }
2998 None
2999 }
3000
3001 pub(super) fn recover_vcs_conflict_marker(&mut self) {
3002 let Some(start) = self.conflict_marker(&TokenKind::Shl, &TokenKind::Lt) else {
3004 return;
3005 };
3006 let mut spans = Vec::with_capacity(3);
3007 spans.push(start);
3008 let mut middlediff3 = None;
3010 let mut middle = None;
3012 let mut end = None;
3014 loop {
3015 if self.token == TokenKind::Eof {
3016 break;
3017 }
3018 if let Some(span) = self.conflict_marker(&TokenKind::OrOr, &TokenKind::Or) {
3019 middlediff3 = Some(span);
3020 }
3021 if let Some(span) = self.conflict_marker(&TokenKind::EqEq, &TokenKind::Eq) {
3022 middle = Some(span);
3023 }
3024 if let Some(span) = self.conflict_marker(&TokenKind::Shr, &TokenKind::Gt) {
3025 spans.push(span);
3026 end = Some(span);
3027 break;
3028 }
3029 self.bump();
3030 }
3031
3032 let mut err = self.dcx().struct_span_fatal(spans, "encountered diff marker");
3033 match middlediff3 {
3034 Some(middlediff3) => {
3036 err.span_label(
3037 start,
3038 "between this marker and `|||||||` is the code that we're merging into",
3039 );
3040 err.span_label(middlediff3, "between this marker and `=======` is the base code (what the two refs diverged from)");
3041 }
3042 None => {
3043 err.span_label(
3044 start,
3045 "between this marker and `=======` is the code that we're merging into",
3046 );
3047 }
3048 };
3049
3050 if let Some(middle) = middle {
3051 err.span_label(middle, "between this marker and `>>>>>>>` is the incoming code");
3052 }
3053 if let Some(end) = end {
3054 err.span_label(end, "this marker concludes the conflict region");
3055 }
3056 err.note(
3057 "conflict markers indicate that a merge was started but could not be completed due \
3058 to merge conflicts\n\
3059 to resolve a conflict, keep only the code you want and then delete the lines \
3060 containing conflict markers",
3061 );
3062 err.help(
3063 "if you're having merge conflicts after pulling new code:\n\
3064 the top section is the code you already had and the bottom section is the remote code\n\
3065 if you're in the middle of a rebase:\n\
3066 the top section is the code being rebased onto and the bottom section is the code \
3067 coming from the current commit being rebased",
3068 );
3069
3070 err.note(
3071 "for an explanation on these markers from the `git` documentation:\n\
3072 visit <https://git-scm.com/book/en/v2/Git-Tools-Advanced-Merging#_checking_out_conflicts>",
3073 );
3074
3075 err.emit();
3076 }
3077
3078 fn skip_pat_list(&mut self) -> PResult<'a, ()> {
3081 while !self.check(exp!(CloseParen)) {
3082 self.parse_pat_no_top_alt(None, None)?;
3083 if !self.eat(exp!(Comma)) {
3084 return Ok(());
3085 }
3086 }
3087 Ok(())
3088 }
3089}