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