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