]>
Commit | Line | Data |
---|---|---|
74b04a01 | 1 | use super::ty::AllowPlus; |
dfeec247 | 2 | use super::{BlockMode, Parser, PathStyle, SemiColonMode, SeqSep, TokenExpectType, TokenType}; |
60c5eb7d | 3 | |
74b04a01 XL |
4 | use rustc_ast::ast::{ |
5 | self, BinOpKind, BindingMode, BlockCheckMode, Expr, ExprKind, Ident, Item, Param, | |
6 | }; | |
7 | use rustc_ast::ast::{AttrVec, ItemKind, Mutability, Pat, PatKind, PathSegment, QSelf, Ty, TyKind}; | |
8 | use rustc_ast::ptr::P; | |
ba9703b0 | 9 | use rustc_ast::token::{self, Lit, LitKind, TokenKind}; |
74b04a01 XL |
10 | use rustc_ast::util::parser::AssocOp; |
11 | use rustc_ast_pretty::pprust; | |
dc9dc135 | 12 | use rustc_data_structures::fx::FxHashSet; |
dfeec247 XL |
13 | use rustc_errors::{pluralize, struct_span_err}; |
14 | use rustc_errors::{Applicability, DiagnosticBuilder, Handler, PResult}; | |
15 | use rustc_span::source_map::Spanned; | |
16 | use rustc_span::symbol::kw; | |
17 | use rustc_span::{MultiSpan, Span, SpanSnippetError, DUMMY_SP}; | |
60c5eb7d | 18 | |
dc9dc135 XL |
19 | use log::{debug, trace}; |
20 | ||
74b04a01 | 21 | const TURBOFISH: &str = "use `::<...>` instead of `<...>` to specify type arguments"; |
e74abb32 | 22 | |
dc9dc135 | 23 | /// Creates a placeholder argument. |
e74abb32 | 24 | pub(super) fn dummy_arg(ident: Ident) -> Param { |
dc9dc135 XL |
25 | let pat = P(Pat { |
26 | id: ast::DUMMY_NODE_ID, | |
dfeec247 | 27 | kind: PatKind::Ident(BindingMode::ByValue(Mutability::Not), ident, None), |
dc9dc135 XL |
28 | span: ident.span, |
29 | }); | |
dfeec247 | 30 | let ty = Ty { kind: TyKind::Err, span: ident.span, id: ast::DUMMY_NODE_ID }; |
e1599b0c | 31 | Param { |
dfeec247 | 32 | attrs: AttrVec::default(), |
e1599b0c XL |
33 | id: ast::DUMMY_NODE_ID, |
34 | pat, | |
35 | span: ident.span, | |
36 | ty: P(ty), | |
37 | is_placeholder: false, | |
38 | } | |
dc9dc135 XL |
39 | } |
40 | ||
41 | pub enum Error { | |
dc9dc135 | 42 | UselessDocComment, |
dc9dc135 XL |
43 | } |
44 | ||
45 | impl Error { | |
dfeec247 | 46 | fn span_err(self, sp: impl Into<MultiSpan>, handler: &Handler) -> DiagnosticBuilder<'_> { |
dc9dc135 | 47 | match self { |
dc9dc135 XL |
48 | Error::UselessDocComment => { |
49 | let mut err = struct_span_err!( | |
50 | handler, | |
51 | sp, | |
52 | E0585, | |
53 | "found a documentation comment that doesn't document anything", | |
54 | ); | |
dfeec247 XL |
55 | err.help( |
56 | "doc comments must come before what they document, maybe a comment was \ | |
57 | intended with `//`?", | |
dc9dc135 | 58 | ); |
dc9dc135 XL |
59 | err |
60 | } | |
61 | } | |
62 | } | |
63 | } | |
48663c56 | 64 | |
e74abb32 | 65 | pub(super) trait RecoverQPath: Sized + 'static { |
48663c56 XL |
66 | const PATH_STYLE: PathStyle = PathStyle::Expr; |
67 | fn to_ty(&self) -> Option<P<Ty>>; | |
68 | fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self; | |
69 | } | |
70 | ||
71 | impl RecoverQPath for Ty { | |
72 | const PATH_STYLE: PathStyle = PathStyle::Type; | |
73 | fn to_ty(&self) -> Option<P<Ty>> { | |
74 | Some(P(self.clone())) | |
75 | } | |
76 | fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self { | |
dfeec247 | 77 | Self { span: path.span, kind: TyKind::Path(qself, path), id: ast::DUMMY_NODE_ID } |
48663c56 XL |
78 | } |
79 | } | |
80 | ||
81 | impl RecoverQPath for Pat { | |
82 | fn to_ty(&self) -> Option<P<Ty>> { | |
83 | self.to_ty() | |
84 | } | |
85 | fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self { | |
dfeec247 | 86 | Self { span: path.span, kind: PatKind::Path(qself, path), id: ast::DUMMY_NODE_ID } |
48663c56 XL |
87 | } |
88 | } | |
89 | ||
90 | impl RecoverQPath for Expr { | |
91 | fn to_ty(&self) -> Option<P<Ty>> { | |
92 | self.to_ty() | |
93 | } | |
94 | fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self { | |
95 | Self { | |
96 | span: path.span, | |
e74abb32 | 97 | kind: ExprKind::Path(qself, path), |
dfeec247 | 98 | attrs: AttrVec::new(), |
48663c56 XL |
99 | id: ast::DUMMY_NODE_ID, |
100 | } | |
101 | } | |
102 | } | |
103 | ||
e74abb32 XL |
104 | /// Control whether the closing delimiter should be consumed when calling `Parser::consume_block`. |
105 | crate enum ConsumeClosingDelim { | |
106 | Yes, | |
107 | No, | |
108 | } | |
109 | ||
48663c56 | 110 | impl<'a> Parser<'a> { |
e74abb32 XL |
111 | pub(super) fn span_fatal_err<S: Into<MultiSpan>>( |
112 | &self, | |
113 | sp: S, | |
114 | err: Error, | |
115 | ) -> DiagnosticBuilder<'a> { | |
dc9dc135 XL |
116 | err.span_err(sp, self.diagnostic()) |
117 | } | |
118 | ||
e74abb32 | 119 | pub fn struct_span_err<S: Into<MultiSpan>>(&self, sp: S, m: &str) -> DiagnosticBuilder<'a> { |
dc9dc135 XL |
120 | self.sess.span_diagnostic.struct_span_err(sp, m) |
121 | } | |
122 | ||
e74abb32 | 123 | pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, m: &str) -> ! { |
dc9dc135 XL |
124 | self.sess.span_diagnostic.span_bug(sp, m) |
125 | } | |
126 | ||
60c5eb7d | 127 | pub(super) fn diagnostic(&self) -> &'a Handler { |
dc9dc135 XL |
128 | &self.sess.span_diagnostic |
129 | } | |
130 | ||
e74abb32 | 131 | pub(super) fn span_to_snippet(&self, span: Span) -> Result<String, SpanSnippetError> { |
416331ca XL |
132 | self.sess.source_map().span_to_snippet(span) |
133 | } | |
134 | ||
e74abb32 | 135 | pub(super) fn expected_ident_found(&self) -> DiagnosticBuilder<'a> { |
dc9dc135 XL |
136 | let mut err = self.struct_span_err( |
137 | self.token.span, | |
dfeec247 | 138 | &format!("expected identifier, found {}", super::token_descr(&self.token)), |
dc9dc135 | 139 | ); |
60c5eb7d XL |
140 | let valid_follow = &[ |
141 | TokenKind::Eq, | |
142 | TokenKind::Colon, | |
143 | TokenKind::Comma, | |
144 | TokenKind::Semi, | |
145 | TokenKind::ModSep, | |
146 | TokenKind::OpenDelim(token::DelimToken::Brace), | |
147 | TokenKind::OpenDelim(token::DelimToken::Paren), | |
148 | TokenKind::CloseDelim(token::DelimToken::Brace), | |
149 | TokenKind::CloseDelim(token::DelimToken::Paren), | |
150 | ]; | |
74b04a01 XL |
151 | match self.token.ident() { |
152 | Some((ident, false)) | |
153 | if ident.is_raw_guess() | |
154 | && self.look_ahead(1, |t| valid_follow.contains(&t.kind)) => | |
60c5eb7d | 155 | { |
dc9dc135 | 156 | err.span_suggestion( |
74b04a01 | 157 | ident.span, |
dc9dc135 | 158 | "you can escape reserved keywords to use them as identifiers", |
74b04a01 | 159 | format!("r#{}", ident.name), |
dc9dc135 XL |
160 | Applicability::MaybeIncorrect, |
161 | ); | |
162 | } | |
74b04a01 | 163 | _ => {} |
dc9dc135 | 164 | } |
dfeec247 | 165 | if let Some(token_descr) = super::token_descr_opt(&self.token) { |
dc9dc135 XL |
166 | err.span_label(self.token.span, format!("expected identifier, found {}", token_descr)); |
167 | } else { | |
168 | err.span_label(self.token.span, "expected identifier"); | |
169 | if self.token == token::Comma && self.look_ahead(1, |t| t.is_ident()) { | |
170 | err.span_suggestion( | |
171 | self.token.span, | |
172 | "remove this comma", | |
173 | String::new(), | |
174 | Applicability::MachineApplicable, | |
175 | ); | |
176 | } | |
177 | } | |
178 | err | |
179 | } | |
180 | ||
e74abb32 | 181 | pub(super) fn expected_one_of_not_found( |
dc9dc135 XL |
182 | &mut self, |
183 | edible: &[TokenKind], | |
184 | inedible: &[TokenKind], | |
185 | ) -> PResult<'a, bool /* recovered */> { | |
186 | fn tokens_to_string(tokens: &[TokenType]) -> String { | |
187 | let mut i = tokens.iter(); | |
e1599b0c | 188 | // This might be a sign we need a connect method on `Iterator`. |
dfeec247 | 189 | let b = i.next().map_or(String::new(), |t| t.to_string()); |
dc9dc135 XL |
190 | i.enumerate().fold(b, |mut b, (i, a)| { |
191 | if tokens.len() > 2 && i == tokens.len() - 2 { | |
192 | b.push_str(", or "); | |
193 | } else if tokens.len() == 2 && i == tokens.len() - 2 { | |
194 | b.push_str(" or "); | |
195 | } else { | |
196 | b.push_str(", "); | |
197 | } | |
198 | b.push_str(&a.to_string()); | |
199 | b | |
200 | }) | |
201 | } | |
202 | ||
dfeec247 XL |
203 | let mut expected = edible |
204 | .iter() | |
dc9dc135 XL |
205 | .map(|x| TokenType::Token(x.clone())) |
206 | .chain(inedible.iter().map(|x| TokenType::Token(x.clone()))) | |
207 | .chain(self.expected_tokens.iter().cloned()) | |
208 | .collect::<Vec<_>>(); | |
209 | expected.sort_by_cached_key(|x| x.to_string()); | |
210 | expected.dedup(); | |
211 | let expect = tokens_to_string(&expected[..]); | |
dfeec247 | 212 | let actual = super::token_descr(&self.token); |
dc9dc135 XL |
213 | let (msg_exp, (label_sp, label_exp)) = if expected.len() > 1 { |
214 | let short_expect = if expected.len() > 6 { | |
215 | format!("{} possible tokens", expected.len()) | |
216 | } else { | |
217 | expect.clone() | |
218 | }; | |
dfeec247 XL |
219 | ( |
220 | format!("expected one of {}, found {}", expect, actual), | |
74b04a01 | 221 | (self.prev_token.span.shrink_to_hi(), format!("expected one of {}", short_expect)), |
dfeec247 | 222 | ) |
dc9dc135 | 223 | } else if expected.is_empty() { |
dfeec247 XL |
224 | ( |
225 | format!("unexpected token: {}", actual), | |
74b04a01 | 226 | (self.prev_token.span, "unexpected token after this".to_string()), |
dfeec247 | 227 | ) |
dc9dc135 | 228 | } else { |
dfeec247 XL |
229 | ( |
230 | format!("expected {}, found {}", expect, actual), | |
74b04a01 | 231 | (self.prev_token.span.shrink_to_hi(), format!("expected {}", expect)), |
dfeec247 | 232 | ) |
dc9dc135 XL |
233 | }; |
234 | self.last_unexpected_token_span = Some(self.token.span); | |
dfeec247 | 235 | let mut err = self.struct_span_err(self.token.span, &msg_exp); |
dc9dc135 | 236 | let sp = if self.token == token::Eof { |
e1599b0c | 237 | // This is EOF; don't want to point at the following char, but rather the last token. |
74b04a01 | 238 | self.prev_token.span |
dc9dc135 XL |
239 | } else { |
240 | label_sp | |
241 | }; | |
dfeec247 XL |
242 | match self.recover_closing_delimiter( |
243 | &expected | |
244 | .iter() | |
245 | .filter_map(|tt| match tt { | |
246 | TokenType::Token(t) => Some(t.clone()), | |
247 | _ => None, | |
248 | }) | |
249 | .collect::<Vec<_>>(), | |
250 | err, | |
251 | ) { | |
dc9dc135 XL |
252 | Err(e) => err = e, |
253 | Ok(recovered) => { | |
254 | return Ok(recovered); | |
255 | } | |
256 | } | |
257 | ||
ba9703b0 XL |
258 | if self.check_too_many_raw_str_terminators(&mut err) { |
259 | return Err(err); | |
260 | } | |
261 | ||
416331ca | 262 | let sm = self.sess.source_map(); |
74b04a01 | 263 | if self.prev_token.span == DUMMY_SP { |
e74abb32 XL |
264 | // Account for macro context where the previous span might not be |
265 | // available to avoid incorrect output (#54841). | |
266 | err.span_label(self.token.span, label_exp); | |
267 | } else if !sm.is_multiline(self.token.span.shrink_to_hi().until(sp.shrink_to_lo())) { | |
268 | // When the spans are in the same line, it means that the only content between | |
269 | // them is whitespace, point at the found token in that case: | |
270 | // | |
271 | // X | () => { syntax error }; | |
272 | // | ^^^^^ expected one of 8 possible tokens here | |
273 | // | |
274 | // instead of having: | |
275 | // | |
276 | // X | () => { syntax error }; | |
277 | // | -^^^^^ unexpected token | |
278 | // | | | |
279 | // | expected one of 8 possible tokens here | |
280 | err.span_label(self.token.span, label_exp); | |
281 | } else { | |
282 | err.span_label(sp, label_exp); | |
283 | err.span_label(self.token.span, "unexpected token"); | |
dc9dc135 | 284 | } |
416331ca | 285 | self.maybe_annotate_with_ascription(&mut err, false); |
dc9dc135 XL |
286 | Err(err) |
287 | } | |
288 | ||
ba9703b0 XL |
289 | fn check_too_many_raw_str_terminators(&mut self, err: &mut DiagnosticBuilder<'_>) -> bool { |
290 | match (&self.prev_token.kind, &self.token.kind) { | |
291 | ( | |
292 | TokenKind::Literal(Lit { | |
293 | kind: LitKind::StrRaw(n_hashes) | LitKind::ByteStrRaw(n_hashes), | |
294 | .. | |
295 | }), | |
296 | TokenKind::Pound, | |
297 | ) => { | |
298 | err.set_primary_message("too many `#` when terminating raw string"); | |
299 | err.span_suggestion( | |
300 | self.token.span, | |
301 | "remove the extra `#`", | |
302 | String::new(), | |
303 | Applicability::MachineApplicable, | |
304 | ); | |
305 | err.note(&format!("the raw string started with {} `#`s", n_hashes)); | |
306 | true | |
307 | } | |
308 | _ => false, | |
309 | } | |
310 | } | |
311 | ||
416331ca | 312 | pub fn maybe_annotate_with_ascription( |
60c5eb7d | 313 | &mut self, |
416331ca XL |
314 | err: &mut DiagnosticBuilder<'_>, |
315 | maybe_expected_semicolon: bool, | |
316 | ) { | |
60c5eb7d | 317 | if let Some((sp, likely_path)) = self.last_type_ascription.take() { |
416331ca XL |
318 | let sm = self.sess.source_map(); |
319 | let next_pos = sm.lookup_char_pos(self.token.span.lo()); | |
320 | let op_pos = sm.lookup_char_pos(sp.hi()); | |
321 | ||
e74abb32 XL |
322 | let allow_unstable = self.sess.unstable_features.is_nightly_build(); |
323 | ||
416331ca XL |
324 | if likely_path { |
325 | err.span_suggestion( | |
326 | sp, | |
327 | "maybe write a path separator here", | |
328 | "::".to_string(), | |
e74abb32 XL |
329 | if allow_unstable { |
330 | Applicability::MaybeIncorrect | |
331 | } else { | |
332 | Applicability::MachineApplicable | |
416331ca XL |
333 | }, |
334 | ); | |
335 | } else if op_pos.line != next_pos.line && maybe_expected_semicolon { | |
336 | err.span_suggestion( | |
337 | sp, | |
338 | "try using a semicolon", | |
339 | ";".to_string(), | |
340 | Applicability::MaybeIncorrect, | |
341 | ); | |
e74abb32 | 342 | } else if allow_unstable { |
416331ca | 343 | err.span_label(sp, "tried to parse a type due to this type ascription"); |
e74abb32 XL |
344 | } else { |
345 | err.span_label(sp, "tried to parse a type due to this"); | |
416331ca | 346 | } |
e74abb32 | 347 | if allow_unstable { |
416331ca | 348 | // Give extra information about type ascription only if it's a nightly compiler. |
dfeec247 XL |
349 | err.note( |
350 | "`#![feature(type_ascription)]` lets you annotate an expression with a \ | |
351 | type: `<expr>: <type>`", | |
352 | ); | |
353 | err.note( | |
74b04a01 XL |
354 | "see issue #23416 <https://github.com/rust-lang/rust/issues/23416> \ |
355 | for more information", | |
dfeec247 | 356 | ); |
416331ca XL |
357 | } |
358 | } | |
359 | } | |
360 | ||
dc9dc135 XL |
361 | /// Eats and discards tokens until one of `kets` is encountered. Respects token trees, |
362 | /// passes through any errors encountered. Used for error recovery. | |
e74abb32 | 363 | pub(super) fn eat_to_tokens(&mut self, kets: &[&TokenKind]) { |
dfeec247 XL |
364 | if let Err(ref mut err) = |
365 | self.parse_seq_to_before_tokens(kets, SeqSep::none(), TokenExpectType::Expect, |p| { | |
366 | Ok(p.parse_token_tree()) | |
367 | }) | |
368 | { | |
e1599b0c | 369 | err.cancel(); |
dc9dc135 XL |
370 | } |
371 | } | |
372 | ||
373 | /// This function checks if there are trailing angle brackets and produces | |
374 | /// a diagnostic to suggest removing them. | |
375 | /// | |
376 | /// ```ignore (diagnostic) | |
377 | /// let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>>>(); | |
378 | /// ^^ help: remove extra angle brackets | |
379 | /// ``` | |
e74abb32 | 380 | pub(super) fn check_trailing_angle_brackets(&mut self, segment: &PathSegment, end: TokenKind) { |
dc9dc135 XL |
381 | // This function is intended to be invoked after parsing a path segment where there are two |
382 | // cases: | |
383 | // | |
384 | // 1. A specific token is expected after the path segment. | |
385 | // eg. `x.foo(`, `x.foo::<u32>(` (parenthesis - method call), | |
386 | // `Foo::`, or `Foo::<Bar>::` (mod sep - continued path). | |
387 | // 2. No specific token is expected after the path segment. | |
388 | // eg. `x.foo` (field access) | |
389 | // | |
390 | // This function is called after parsing `.foo` and before parsing the token `end` (if | |
391 | // present). This includes any angle bracket arguments, such as `.foo::<u32>` or | |
392 | // `Foo::<Bar>`. | |
393 | ||
394 | // We only care about trailing angle brackets if we previously parsed angle bracket | |
395 | // arguments. This helps stop us incorrectly suggesting that extra angle brackets be | |
396 | // removed in this case: | |
397 | // | |
398 | // `x.foo >> (3)` (where `x.foo` is a `u32` for example) | |
399 | // | |
400 | // This case is particularly tricky as we won't notice it just looking at the tokens - | |
401 | // it will appear the same (in terms of upcoming tokens) as below (since the `::<u32>` will | |
402 | // have already been parsed): | |
403 | // | |
404 | // `x.foo::<u32>>>(3)` | |
dfeec247 XL |
405 | let parsed_angle_bracket_args = |
406 | segment.args.as_ref().map(|args| args.is_angle_bracketed()).unwrap_or(false); | |
dc9dc135 XL |
407 | |
408 | debug!( | |
409 | "check_trailing_angle_brackets: parsed_angle_bracket_args={:?}", | |
410 | parsed_angle_bracket_args, | |
411 | ); | |
412 | if !parsed_angle_bracket_args { | |
413 | return; | |
414 | } | |
415 | ||
416 | // Keep the span at the start so we can highlight the sequence of `>` characters to be | |
417 | // removed. | |
418 | let lo = self.token.span; | |
419 | ||
420 | // We need to look-ahead to see if we have `>` characters without moving the cursor forward | |
421 | // (since we might have the field access case and the characters we're eating are | |
422 | // actual operators and not trailing characters - ie `x.foo >> 3`). | |
423 | let mut position = 0; | |
424 | ||
425 | // We can encounter `>` or `>>` tokens in any order, so we need to keep track of how | |
426 | // many of each (so we can correctly pluralize our error messages) and continue to | |
427 | // advance. | |
428 | let mut number_of_shr = 0; | |
429 | let mut number_of_gt = 0; | |
430 | while self.look_ahead(position, |t| { | |
431 | trace!("check_trailing_angle_brackets: t={:?}", t); | |
432 | if *t == token::BinOp(token::BinOpToken::Shr) { | |
433 | number_of_shr += 1; | |
434 | true | |
435 | } else if *t == token::Gt { | |
436 | number_of_gt += 1; | |
437 | true | |
438 | } else { | |
439 | false | |
440 | } | |
441 | }) { | |
442 | position += 1; | |
443 | } | |
444 | ||
445 | // If we didn't find any trailing `>` characters, then we have nothing to error about. | |
446 | debug!( | |
447 | "check_trailing_angle_brackets: number_of_gt={:?} number_of_shr={:?}", | |
448 | number_of_gt, number_of_shr, | |
449 | ); | |
450 | if number_of_gt < 1 && number_of_shr < 1 { | |
451 | return; | |
452 | } | |
453 | ||
454 | // Finally, double check that we have our end token as otherwise this is the | |
455 | // second case. | |
456 | if self.look_ahead(position, |t| { | |
457 | trace!("check_trailing_angle_brackets: t={:?}", t); | |
458 | *t == end | |
459 | }) { | |
460 | // Eat from where we started until the end token so that parsing can continue | |
461 | // as if we didn't have those extra angle brackets. | |
462 | self.eat_to_tokens(&[&end]); | |
463 | let span = lo.until(self.token.span); | |
464 | ||
e1599b0c | 465 | let total_num_of_gt = number_of_gt + number_of_shr * 2; |
dfeec247 XL |
466 | self.struct_span_err( |
467 | span, | |
468 | &format!("unmatched angle bracket{}", pluralize!(total_num_of_gt)), | |
469 | ) | |
470 | .span_suggestion( | |
471 | span, | |
472 | &format!("remove extra angle bracket{}", pluralize!(total_num_of_gt)), | |
473 | String::new(), | |
474 | Applicability::MachineApplicable, | |
475 | ) | |
476 | .emit(); | |
477 | } | |
478 | } | |
479 | ||
480 | /// Check to see if a pair of chained operators looks like an attempt at chained comparison, | |
481 | /// e.g. `1 < x <= 3`. If so, suggest either splitting the comparison into two, or | |
482 | /// parenthesising the leftmost comparison. | |
483 | fn attempt_chained_comparison_suggestion( | |
484 | &mut self, | |
485 | err: &mut DiagnosticBuilder<'_>, | |
486 | inner_op: &Expr, | |
487 | outer_op: &Spanned<AssocOp>, | |
ba9703b0 | 488 | ) -> bool /* advanced the cursor */ { |
dfeec247 | 489 | if let ExprKind::Binary(op, ref l1, ref r1) = inner_op.kind { |
ba9703b0 XL |
490 | if let ExprKind::Field(_, ident) = l1.kind { |
491 | if ident.as_str().parse::<i32>().is_err() && !matches!(r1.kind, ExprKind::Lit(_)) { | |
492 | // The parser has encountered `foo.bar<baz`, the likelihood of the turbofish | |
493 | // suggestion being the only one to apply is high. | |
494 | return false; | |
495 | } | |
496 | } | |
497 | let mut enclose = |left: Span, right: Span| { | |
498 | err.multipart_suggestion( | |
499 | "parenthesize the comparison", | |
500 | vec![ | |
501 | (left.shrink_to_lo(), "(".to_string()), | |
502 | (right.shrink_to_hi(), ")".to_string()), | |
503 | ], | |
504 | Applicability::MaybeIncorrect, | |
505 | ); | |
506 | }; | |
507 | return match (op.node, &outer_op.node) { | |
508 | // `x == y == z` | |
509 | (BinOpKind::Eq, AssocOp::Equal) | | |
dfeec247 | 510 | // `x < y < z` and friends. |
ba9703b0 XL |
511 | (BinOpKind::Lt, AssocOp::Less | AssocOp::LessEqual) | |
512 | (BinOpKind::Le, AssocOp::LessEqual | AssocOp::Less) | | |
dfeec247 | 513 | // `x > y > z` and friends. |
ba9703b0 XL |
514 | (BinOpKind::Gt, AssocOp::Greater | AssocOp::GreaterEqual) | |
515 | (BinOpKind::Ge, AssocOp::GreaterEqual | AssocOp::Greater) => { | |
dfeec247 XL |
516 | let expr_to_str = |e: &Expr| { |
517 | self.span_to_snippet(e.span) | |
518 | .unwrap_or_else(|_| pprust::expr_to_string(&e)) | |
519 | }; | |
ba9703b0 XL |
520 | err.span_suggestion_verbose( |
521 | inner_op.span.shrink_to_hi(), | |
522 | "split the comparison into two", | |
523 | format!(" && {}", expr_to_str(&r1)), | |
dfeec247 XL |
524 | Applicability::MaybeIncorrect, |
525 | ); | |
ba9703b0 | 526 | false // Keep the current parse behavior, where the AST is `(x < y) < z`. |
dfeec247 | 527 | } |
ba9703b0 XL |
528 | // `x == y < z` |
529 | (BinOpKind::Eq, AssocOp::Less | AssocOp::LessEqual | AssocOp::Greater | AssocOp::GreaterEqual) => { | |
530 | // Consume `z`/outer-op-rhs. | |
531 | let snapshot = self.clone(); | |
532 | match self.parse_expr() { | |
533 | Ok(r2) => { | |
534 | // We are sure that outer-op-rhs could be consumed, the suggestion is | |
535 | // likely correct. | |
536 | enclose(r1.span, r2.span); | |
537 | true | |
538 | } | |
539 | Err(mut expr_err) => { | |
540 | expr_err.cancel(); | |
541 | *self = snapshot; | |
542 | false | |
543 | } | |
544 | } | |
545 | } | |
546 | // `x > y == z` | |
547 | (BinOpKind::Lt | BinOpKind::Le | BinOpKind::Gt | BinOpKind::Ge, AssocOp::Equal) => { | |
548 | let snapshot = self.clone(); | |
549 | // At this point it is always valid to enclose the lhs in parentheses, no | |
550 | // further checks are necessary. | |
551 | match self.parse_expr() { | |
552 | Ok(_) => { | |
553 | enclose(l1.span, r1.span); | |
554 | true | |
555 | } | |
556 | Err(mut expr_err) => { | |
557 | expr_err.cancel(); | |
558 | *self = snapshot; | |
559 | false | |
560 | } | |
561 | } | |
562 | } | |
563 | _ => false, | |
564 | }; | |
dc9dc135 | 565 | } |
ba9703b0 | 566 | false |
dc9dc135 XL |
567 | } |
568 | ||
e1599b0c | 569 | /// Produces an error if comparison operators are chained (RFC #558). |
e74abb32 XL |
570 | /// We only need to check the LHS, not the RHS, because all comparison ops have same |
571 | /// precedence (see `fn precedence`) and are left-associative (see `fn fixity`). | |
572 | /// | |
573 | /// This can also be hit if someone incorrectly writes `foo<bar>()` when they should have used | |
574 | /// the turbofish (`foo::<bar>()`) syntax. We attempt some heuristic recovery if that is the | |
575 | /// case. | |
576 | /// | |
577 | /// Keep in mind that given that `outer_op.is_comparison()` holds and comparison ops are left | |
578 | /// associative we can infer that we have: | |
579 | /// | |
ba9703b0 | 580 | /// ```text |
e74abb32 XL |
581 | /// outer_op |
582 | /// / \ | |
583 | /// inner_op r2 | |
584 | /// / \ | |
dfeec247 | 585 | /// l1 r1 |
ba9703b0 | 586 | /// ``` |
e74abb32 XL |
587 | pub(super) fn check_no_chained_comparison( |
588 | &mut self, | |
dfeec247 XL |
589 | inner_op: &Expr, |
590 | outer_op: &Spanned<AssocOp>, | |
e74abb32 XL |
591 | ) -> PResult<'a, Option<P<Expr>>> { |
592 | debug_assert!( | |
dfeec247 | 593 | outer_op.node.is_comparison(), |
e74abb32 | 594 | "check_no_chained_comparison: {:?} is not comparison", |
dfeec247 | 595 | outer_op.node, |
e74abb32 XL |
596 | ); |
597 | ||
dfeec247 XL |
598 | let mk_err_expr = |
599 | |this: &Self, span| Ok(Some(this.mk_expr(span, ExprKind::Err, AttrVec::new()))); | |
e74abb32 | 600 | |
dfeec247 | 601 | match inner_op.kind { |
ba9703b0 XL |
602 | ExprKind::Binary(op, ref l1, ref r1) if op.node.is_comparison() => { |
603 | let mut err = self.struct_span_err( | |
604 | vec![op.span, self.prev_token.span], | |
605 | "comparison operators cannot be chained", | |
606 | ); | |
e74abb32 XL |
607 | |
608 | let suggest = |err: &mut DiagnosticBuilder<'_>| { | |
609 | err.span_suggestion_verbose( | |
ba9703b0 | 610 | op.span.shrink_to_lo(), |
e74abb32 XL |
611 | TURBOFISH, |
612 | "::".to_string(), | |
613 | Applicability::MaybeIncorrect, | |
614 | ); | |
615 | }; | |
616 | ||
ba9703b0 XL |
617 | // Include `<` to provide this recommendation even in a case like |
618 | // `Foo<Bar<Baz<Qux, ()>>>` | |
619 | if op.node == BinOpKind::Lt && outer_op.node == AssocOp::Less | |
620 | || outer_op.node == AssocOp::Greater | |
dfeec247 | 621 | { |
dfeec247 | 622 | if outer_op.node == AssocOp::Less { |
e74abb32 XL |
623 | let snapshot = self.clone(); |
624 | self.bump(); | |
625 | // So far we have parsed `foo<bar<`, consume the rest of the type args. | |
dfeec247 XL |
626 | let modifiers = |
627 | [(token::Lt, 1), (token::Gt, -1), (token::BinOp(token::Shr), -2)]; | |
e74abb32 XL |
628 | self.consume_tts(1, &modifiers[..]); |
629 | ||
dfeec247 XL |
630 | if !&[token::OpenDelim(token::Paren), token::ModSep] |
631 | .contains(&self.token.kind) | |
632 | { | |
e74abb32 XL |
633 | // We don't have `foo< bar >(` or `foo< bar >::`, so we rewind the |
634 | // parser and bail out. | |
ba9703b0 | 635 | *self = snapshot.clone(); |
e74abb32 XL |
636 | } |
637 | } | |
638 | return if token::ModSep == self.token.kind { | |
639 | // We have some certainty that this was a bad turbofish at this point. | |
640 | // `foo< bar >::` | |
641 | suggest(&mut err); | |
642 | ||
643 | let snapshot = self.clone(); | |
644 | self.bump(); // `::` | |
645 | ||
646 | // Consume the rest of the likely `foo<bar>::new()` or return at `foo<bar>`. | |
647 | match self.parse_expr() { | |
648 | Ok(_) => { | |
649 | // 99% certain that the suggestion is correct, continue parsing. | |
650 | err.emit(); | |
651 | // FIXME: actually check that the two expressions in the binop are | |
652 | // paths and resynthesize new fn call expression instead of using | |
653 | // `ExprKind::Err` placeholder. | |
74b04a01 | 654 | mk_err_expr(self, inner_op.span.to(self.prev_token.span)) |
e74abb32 XL |
655 | } |
656 | Err(mut expr_err) => { | |
657 | expr_err.cancel(); | |
658 | // Not entirely sure now, but we bubble the error up with the | |
659 | // suggestion. | |
ba9703b0 | 660 | *self = snapshot; |
e74abb32 XL |
661 | Err(err) |
662 | } | |
663 | } | |
664 | } else if token::OpenDelim(token::Paren) == self.token.kind { | |
665 | // We have high certainty that this was a bad turbofish at this point. | |
666 | // `foo< bar >(` | |
667 | suggest(&mut err); | |
668 | // Consume the fn call arguments. | |
669 | match self.consume_fn_args() { | |
670 | Err(()) => Err(err), | |
671 | Ok(()) => { | |
672 | err.emit(); | |
673 | // FIXME: actually check that the two expressions in the binop are | |
674 | // paths and resynthesize new fn call expression instead of using | |
675 | // `ExprKind::Err` placeholder. | |
74b04a01 | 676 | mk_err_expr(self, inner_op.span.to(self.prev_token.span)) |
e74abb32 XL |
677 | } |
678 | } | |
679 | } else { | |
ba9703b0 XL |
680 | if !matches!(l1.kind, ExprKind::Lit(_)) |
681 | && !matches!(r1.kind, ExprKind::Lit(_)) | |
682 | { | |
683 | // All we know is that this is `foo < bar >` and *nothing* else. Try to | |
684 | // be helpful, but don't attempt to recover. | |
685 | err.help(TURBOFISH); | |
686 | err.help("or use `(...)` if you meant to specify fn arguments"); | |
687 | } | |
688 | ||
689 | // If it looks like a genuine attempt to chain operators (as opposed to a | |
690 | // misformatted turbofish, for instance), suggest a correct form. | |
691 | if self.attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op) | |
692 | { | |
693 | err.emit(); | |
694 | mk_err_expr(self, inner_op.span.to(self.prev_token.span)) | |
695 | } else { | |
696 | // These cases cause too many knock-down errors, bail out (#61329). | |
697 | Err(err) | |
698 | } | |
e74abb32 | 699 | }; |
dc9dc135 | 700 | } |
ba9703b0 XL |
701 | let recover = |
702 | self.attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op); | |
dc9dc135 | 703 | err.emit(); |
ba9703b0 XL |
704 | if recover { |
705 | return mk_err_expr(self, inner_op.span.to(self.prev_token.span)); | |
706 | } | |
dc9dc135 XL |
707 | } |
708 | _ => {} | |
709 | } | |
e74abb32 XL |
710 | Ok(None) |
711 | } | |
712 | ||
713 | fn consume_fn_args(&mut self) -> Result<(), ()> { | |
714 | let snapshot = self.clone(); | |
715 | self.bump(); // `(` | |
716 | ||
717 | // Consume the fn call arguments. | |
dfeec247 XL |
718 | let modifiers = |
719 | [(token::OpenDelim(token::Paren), 1), (token::CloseDelim(token::Paren), -1)]; | |
e74abb32 XL |
720 | self.consume_tts(1, &modifiers[..]); |
721 | ||
722 | if self.token.kind == token::Eof { | |
723 | // Not entirely sure that what we consumed were fn arguments, rollback. | |
ba9703b0 | 724 | *self = snapshot; |
e74abb32 XL |
725 | Err(()) |
726 | } else { | |
727 | // 99% certain that the suggestion is correct, continue parsing. | |
728 | Ok(()) | |
729 | } | |
dc9dc135 XL |
730 | } |
731 | ||
e74abb32 | 732 | pub(super) fn maybe_report_ambiguous_plus( |
48663c56 | 733 | &mut self, |
74b04a01 | 734 | allow_plus: AllowPlus, |
48663c56 XL |
735 | impl_dyn_multi: bool, |
736 | ty: &Ty, | |
737 | ) { | |
74b04a01 | 738 | if matches!(allow_plus, AllowPlus::No) && impl_dyn_multi { |
48663c56 XL |
739 | let sum_with_parens = format!("({})", pprust::ty_to_string(&ty)); |
740 | self.struct_span_err(ty.span, "ambiguous `+` in a type") | |
741 | .span_suggestion( | |
742 | ty.span, | |
743 | "use parentheses to disambiguate", | |
744 | sum_with_parens, | |
745 | Applicability::MachineApplicable, | |
746 | ) | |
747 | .emit(); | |
748 | } | |
749 | } | |
750 | ||
e74abb32 | 751 | pub(super) fn maybe_recover_from_bad_type_plus( |
48663c56 | 752 | &mut self, |
74b04a01 | 753 | allow_plus: AllowPlus, |
48663c56 XL |
754 | ty: &Ty, |
755 | ) -> PResult<'a, ()> { | |
756 | // Do not add `+` to expected tokens. | |
74b04a01 | 757 | if matches!(allow_plus, AllowPlus::No) || !self.token.is_like_plus() { |
48663c56 XL |
758 | return Ok(()); |
759 | } | |
760 | ||
761 | self.bump(); // `+` | |
762 | let bounds = self.parse_generic_bounds(None)?; | |
74b04a01 | 763 | let sum_span = ty.span.to(self.prev_token.span); |
48663c56 XL |
764 | |
765 | let mut err = struct_span_err!( | |
766 | self.sess.span_diagnostic, | |
767 | sum_span, | |
768 | E0178, | |
769 | "expected a path on the left-hand side of `+`, not `{}`", | |
770 | pprust::ty_to_string(ty) | |
771 | ); | |
772 | ||
e74abb32 | 773 | match ty.kind { |
48663c56 XL |
774 | TyKind::Rptr(ref lifetime, ref mut_ty) => { |
775 | let sum_with_parens = pprust::to_string(|s| { | |
416331ca XL |
776 | s.s.word("&"); |
777 | s.print_opt_lifetime(lifetime); | |
60c5eb7d | 778 | s.print_mutability(mut_ty.mutbl, false); |
416331ca XL |
779 | s.popen(); |
780 | s.print_type(&mut_ty.ty); | |
781 | s.print_type_bounds(" +", &bounds); | |
48663c56 XL |
782 | s.pclose() |
783 | }); | |
784 | err.span_suggestion( | |
785 | sum_span, | |
786 | "try adding parentheses", | |
787 | sum_with_parens, | |
788 | Applicability::MachineApplicable, | |
789 | ); | |
790 | } | |
791 | TyKind::Ptr(..) | TyKind::BareFn(..) => { | |
792 | err.span_label(sum_span, "perhaps you forgot parentheses?"); | |
793 | } | |
794 | _ => { | |
795 | err.span_label(sum_span, "expected a path"); | |
796 | } | |
797 | } | |
798 | err.emit(); | |
799 | Ok(()) | |
800 | } | |
801 | ||
e1599b0c XL |
802 | /// Tries to recover from associated item paths like `[T]::AssocItem` / `(T, U)::AssocItem`. |
803 | /// Attempts to convert the base expression/pattern/type into a type, parses the `::AssocItem` | |
804 | /// tail, and combines them into a `<Ty>::AssocItem` expression/pattern/type. | |
e74abb32 | 805 | pub(super) fn maybe_recover_from_bad_qpath<T: RecoverQPath>( |
48663c56 XL |
806 | &mut self, |
807 | base: P<T>, | |
808 | allow_recovery: bool, | |
809 | ) -> PResult<'a, P<T>> { | |
810 | // Do not add `::` to expected tokens. | |
811 | if allow_recovery && self.token == token::ModSep { | |
812 | if let Some(ty) = base.to_ty() { | |
813 | return self.maybe_recover_from_bad_qpath_stage_2(ty.span, ty); | |
814 | } | |
815 | } | |
816 | Ok(base) | |
817 | } | |
818 | ||
e1599b0c XL |
819 | /// Given an already parsed `Ty`, parses the `::AssocItem` tail and |
820 | /// combines them into a `<Ty>::AssocItem` expression/pattern/type. | |
e74abb32 | 821 | pub(super) fn maybe_recover_from_bad_qpath_stage_2<T: RecoverQPath>( |
48663c56 XL |
822 | &mut self, |
823 | ty_span: Span, | |
824 | ty: P<Ty>, | |
825 | ) -> PResult<'a, P<T>> { | |
826 | self.expect(&token::ModSep)?; | |
827 | ||
dfeec247 | 828 | let mut path = ast::Path { segments: Vec::new(), span: DUMMY_SP }; |
48663c56 | 829 | self.parse_path_segments(&mut path.segments, T::PATH_STYLE)?; |
74b04a01 | 830 | path.span = ty_span.to(self.prev_token.span); |
48663c56 | 831 | |
dfeec247 XL |
832 | let ty_str = self.span_to_snippet(ty_span).unwrap_or_else(|_| pprust::ty_to_string(&ty)); |
833 | self.struct_span_err(path.span, "missing angle brackets in associated item path") | |
48663c56 | 834 | .span_suggestion( |
e1599b0c | 835 | // This is a best-effort recovery. |
48663c56 XL |
836 | path.span, |
837 | "try", | |
e74abb32 | 838 | format!("<{}>::{}", ty_str, pprust::path_to_string(&path)), |
48663c56 XL |
839 | Applicability::MaybeIncorrect, |
840 | ) | |
841 | .emit(); | |
842 | ||
e1599b0c | 843 | let path_span = ty_span.shrink_to_hi(); // Use an empty path since `position == 0`. |
dfeec247 | 844 | Ok(P(T::recovered(Some(QSelf { ty, path_span, position: 0 }), path))) |
48663c56 XL |
845 | } |
846 | ||
e74abb32 | 847 | pub(super) fn maybe_consume_incorrect_semicolon(&mut self, items: &[P<Item>]) -> bool { |
48663c56 | 848 | if self.eat(&token::Semi) { |
74b04a01 | 849 | let mut err = self.struct_span_err(self.prev_token.span, "expected item, found `;`"); |
48663c56 | 850 | err.span_suggestion_short( |
74b04a01 | 851 | self.prev_token.span, |
48663c56 XL |
852 | "remove this semicolon", |
853 | String::new(), | |
854 | Applicability::MachineApplicable, | |
855 | ); | |
856 | if !items.is_empty() { | |
857 | let previous_item = &items[items.len() - 1]; | |
e74abb32 | 858 | let previous_item_kind_name = match previous_item.kind { |
e1599b0c XL |
859 | // Say "braced struct" because tuple-structs and |
860 | // braceless-empty-struct declarations do take a semicolon. | |
48663c56 XL |
861 | ItemKind::Struct(..) => Some("braced struct"), |
862 | ItemKind::Enum(..) => Some("enum"), | |
863 | ItemKind::Trait(..) => Some("trait"), | |
864 | ItemKind::Union(..) => Some("union"), | |
865 | _ => None, | |
866 | }; | |
867 | if let Some(name) = previous_item_kind_name { | |
dfeec247 | 868 | err.help(&format!("{} declarations are not followed by a semicolon", name)); |
48663c56 XL |
869 | } |
870 | } | |
871 | err.emit(); | |
872 | true | |
873 | } else { | |
874 | false | |
875 | } | |
876 | } | |
877 | ||
e1599b0c | 878 | /// Creates a `DiagnosticBuilder` for an unexpected token `t` and tries to recover if it is a |
dc9dc135 | 879 | /// closing delimiter. |
e74abb32 | 880 | pub(super) fn unexpected_try_recover( |
dc9dc135 XL |
881 | &mut self, |
882 | t: &TokenKind, | |
883 | ) -> PResult<'a, bool /* recovered */> { | |
884 | let token_str = pprust::token_kind_to_string(t); | |
dfeec247 | 885 | let this_token_str = super::token_descr(&self.token); |
dc9dc135 XL |
886 | let (prev_sp, sp) = match (&self.token.kind, self.subparser_name) { |
887 | // Point at the end of the macro call when reaching end of macro arguments. | |
888 | (token::Eof, Some(_)) => { | |
889 | let sp = self.sess.source_map().next_point(self.token.span); | |
890 | (sp, sp) | |
891 | } | |
892 | // We don't want to point at the following span after DUMMY_SP. | |
893 | // This happens when the parser finds an empty TokenStream. | |
74b04a01 | 894 | _ if self.prev_token.span == DUMMY_SP => (self.token.span, self.token.span), |
dc9dc135 | 895 | // EOF, don't want to point at the following char, but rather the last token. |
74b04a01 XL |
896 | (token::Eof, None) => (self.prev_token.span, self.token.span), |
897 | _ => (self.prev_token.span.shrink_to_hi(), self.token.span), | |
dc9dc135 XL |
898 | }; |
899 | let msg = format!( | |
900 | "expected `{}`, found {}", | |
901 | token_str, | |
902 | match (&self.token.kind, self.subparser_name) { | |
903 | (token::Eof, Some(origin)) => format!("end of {}", origin), | |
904 | _ => this_token_str, | |
905 | }, | |
906 | ); | |
907 | let mut err = self.struct_span_err(sp, &msg); | |
908 | let label_exp = format!("expected `{}`", token_str); | |
909 | match self.recover_closing_delimiter(&[t.clone()], err) { | |
910 | Err(e) => err = e, | |
911 | Ok(recovered) => { | |
912 | return Ok(recovered); | |
913 | } | |
914 | } | |
416331ca | 915 | let sm = self.sess.source_map(); |
e74abb32 XL |
916 | if !sm.is_multiline(prev_sp.until(sp)) { |
917 | // When the spans are in the same line, it means that the only content | |
918 | // between them is whitespace, point only at the found token. | |
919 | err.span_label(sp, label_exp); | |
920 | } else { | |
921 | err.span_label(prev_sp, label_exp); | |
922 | err.span_label(sp, "unexpected token"); | |
dc9dc135 XL |
923 | } |
924 | Err(err) | |
925 | } | |
926 | ||
e74abb32 XL |
927 | pub(super) fn expect_semi(&mut self) -> PResult<'a, ()> { |
928 | if self.eat(&token::Semi) { | |
929 | return Ok(()); | |
930 | } | |
931 | let sm = self.sess.source_map(); | |
dfeec247 | 932 | let msg = format!("expected `;`, found `{}`", super::token_descr(&self.token)); |
e74abb32 | 933 | let appl = Applicability::MachineApplicable; |
74b04a01 XL |
934 | if self.token.span == DUMMY_SP || self.prev_token.span == DUMMY_SP { |
935 | // Likely inside a macro, can't provide meaningful suggestions. | |
dfeec247 | 936 | return self.expect(&token::Semi).map(drop); |
74b04a01 | 937 | } else if !sm.is_multiline(self.prev_token.span.until(self.token.span)) { |
e74abb32 | 938 | // The current token is in the same line as the prior token, not recoverable. |
dfeec247 | 939 | } else if self.look_ahead(1, |t| { |
74b04a01 | 940 | t == &token::CloseDelim(token::Brace) || t.can_begin_expr() && t.kind != token::Colon |
dfeec247 XL |
941 | }) && [token::Comma, token::Colon].contains(&self.token.kind) |
942 | { | |
e74abb32 XL |
943 | // Likely typo: `,` → `;` or `:` → `;`. This is triggered if the current token is |
944 | // either `,` or `:`, and the next token could either start a new statement or is a | |
945 | // block close. For example: | |
946 | // | |
947 | // let x = 32: | |
948 | // let y = 42; | |
949 | self.bump(); | |
74b04a01 | 950 | let sp = self.prev_token.span; |
e74abb32 XL |
951 | self.struct_span_err(sp, &msg) |
952 | .span_suggestion(sp, "change this to `;`", ";".to_string(), appl) | |
953 | .emit(); | |
dfeec247 XL |
954 | return Ok(()); |
955 | } else if self.look_ahead(0, |t| { | |
956 | t == &token::CloseDelim(token::Brace) | |
957 | || ( | |
74b04a01 | 958 | t.can_begin_expr() && t != &token::Semi && t != &token::Pound |
dfeec247 XL |
959 | // Avoid triggering with too many trailing `#` in raw string. |
960 | ) | |
961 | }) { | |
e74abb32 XL |
962 | // Missing semicolon typo. This is triggered if the next token could either start a |
963 | // new statement or is a block close. For example: | |
964 | // | |
965 | // let x = 32 | |
966 | // let y = 42; | |
74b04a01 | 967 | let sp = self.prev_token.span.shrink_to_hi(); |
e74abb32 XL |
968 | self.struct_span_err(sp, &msg) |
969 | .span_label(self.token.span, "unexpected token") | |
970 | .span_suggestion_short(sp, "add `;` here", ";".to_string(), appl) | |
971 | .emit(); | |
dfeec247 | 972 | return Ok(()); |
e74abb32 | 973 | } |
dfeec247 | 974 | self.expect(&token::Semi).map(drop) // Error unconditionally |
e74abb32 XL |
975 | } |
976 | ||
e1599b0c | 977 | /// Consumes alternative await syntaxes like `await!(<expr>)`, `await <expr>`, |
416331ca | 978 | /// `await? <expr>`, `await(<expr>)`, and `await { <expr> }`. |
dfeec247 | 979 | pub(super) fn recover_incorrect_await_syntax( |
48663c56 XL |
980 | &mut self, |
981 | lo: Span, | |
982 | await_sp: Span, | |
dfeec247 XL |
983 | attrs: AttrVec, |
984 | ) -> PResult<'a, P<Expr>> { | |
985 | let (hi, expr, is_question) = if self.token == token::Not { | |
416331ca | 986 | // Handle `await!(<expr>)`. |
dfeec247 XL |
987 | self.recover_await_macro()? |
988 | } else { | |
989 | self.recover_await_prefix(await_sp)? | |
990 | }; | |
991 | let sp = self.error_on_incorrect_await(lo, hi, &expr, is_question); | |
992 | let expr = self.mk_expr(lo.to(sp), ExprKind::Await(expr), attrs); | |
993 | self.maybe_recover_from_bad_qpath(expr, true) | |
994 | } | |
995 | ||
996 | fn recover_await_macro(&mut self) -> PResult<'a, (Span, P<Expr>, bool)> { | |
997 | self.expect(&token::Not)?; | |
998 | self.expect(&token::OpenDelim(token::Paren))?; | |
999 | let expr = self.parse_expr()?; | |
1000 | self.expect(&token::CloseDelim(token::Paren))?; | |
74b04a01 | 1001 | Ok((self.prev_token.span, expr, false)) |
dfeec247 | 1002 | } |
416331ca | 1003 | |
dfeec247 | 1004 | fn recover_await_prefix(&mut self, await_sp: Span) -> PResult<'a, (Span, P<Expr>, bool)> { |
48663c56 XL |
1005 | let is_question = self.eat(&token::Question); // Handle `await? <expr>`. |
1006 | let expr = if self.token == token::OpenDelim(token::Brace) { | |
1007 | // Handle `await { <expr> }`. | |
1008 | // This needs to be handled separatedly from the next arm to avoid | |
1009 | // interpreting `await { <expr> }?` as `<expr>?.await`. | |
dfeec247 | 1010 | self.parse_block_expr(None, self.token.span, BlockCheckMode::Default, AttrVec::new()) |
48663c56 XL |
1011 | } else { |
1012 | self.parse_expr() | |
dfeec247 XL |
1013 | } |
1014 | .map_err(|mut err| { | |
48663c56 XL |
1015 | err.span_label(await_sp, "while parsing this incorrect await expression"); |
1016 | err | |
1017 | })?; | |
dfeec247 | 1018 | Ok((expr.span, expr, is_question)) |
416331ca XL |
1019 | } |
1020 | ||
1021 | fn error_on_incorrect_await(&self, lo: Span, hi: Span, expr: &Expr, is_question: bool) -> Span { | |
dfeec247 XL |
1022 | let expr_str = |
1023 | self.span_to_snippet(expr.span).unwrap_or_else(|_| pprust::expr_to_string(&expr)); | |
48663c56 | 1024 | let suggestion = format!("{}.await{}", expr_str, if is_question { "?" } else { "" }); |
416331ca | 1025 | let sp = lo.to(hi); |
e74abb32 | 1026 | let app = match expr.kind { |
48663c56 XL |
1027 | ExprKind::Try(_) => Applicability::MaybeIncorrect, // `await <expr>?` |
1028 | _ => Applicability::MachineApplicable, | |
1029 | }; | |
1030 | self.struct_span_err(sp, "incorrect use of `await`") | |
1031 | .span_suggestion(sp, "`await` is a postfix operation", suggestion, app) | |
1032 | .emit(); | |
416331ca | 1033 | sp |
48663c56 XL |
1034 | } |
1035 | ||
e1599b0c | 1036 | /// If encountering `future.await()`, consumes and emits an error. |
e74abb32 | 1037 | pub(super) fn recover_from_await_method_call(&mut self) { |
dfeec247 XL |
1038 | if self.token == token::OpenDelim(token::Paren) |
1039 | && self.look_ahead(1, |t| t == &token::CloseDelim(token::Paren)) | |
48663c56 XL |
1040 | { |
1041 | // future.await() | |
dc9dc135 | 1042 | let lo = self.token.span; |
48663c56 | 1043 | self.bump(); // ( |
dc9dc135 | 1044 | let sp = lo.to(self.token.span); |
48663c56 XL |
1045 | self.bump(); // ) |
1046 | self.struct_span_err(sp, "incorrect use of `await`") | |
1047 | .span_suggestion( | |
1048 | sp, | |
1049 | "`await` is not a method call, remove the parentheses", | |
1050 | String::new(), | |
1051 | Applicability::MachineApplicable, | |
dfeec247 | 1052 | ) |
74b04a01 | 1053 | .emit(); |
48663c56 XL |
1054 | } |
1055 | } | |
1056 | ||
ba9703b0 XL |
1057 | pub(super) fn try_macro_suggestion(&mut self) -> PResult<'a, P<Expr>> { |
1058 | let is_try = self.token.is_keyword(kw::Try); | |
1059 | let is_questionmark = self.look_ahead(1, |t| t == &token::Not); //check for ! | |
1060 | let is_open = self.look_ahead(2, |t| t == &token::OpenDelim(token::Paren)); //check for ( | |
1061 | ||
1062 | if is_try && is_questionmark && is_open { | |
1063 | let lo = self.token.span; | |
1064 | self.bump(); //remove try | |
1065 | self.bump(); //remove ! | |
1066 | let try_span = lo.to(self.token.span); //we take the try!( span | |
1067 | self.bump(); //remove ( | |
1068 | let is_empty = self.token == token::CloseDelim(token::Paren); //check if the block is empty | |
1069 | self.consume_block(token::Paren, ConsumeClosingDelim::No); //eat the block | |
1070 | let hi = self.token.span; | |
1071 | self.bump(); //remove ) | |
1072 | let mut err = self.struct_span_err(lo.to(hi), "use of deprecated `try` macro"); | |
1073 | err.note("in the 2018 edition `try` is a reserved keyword, and the `try!()` macro is deprecated"); | |
1074 | let prefix = if is_empty { "" } else { "alternatively, " }; | |
1075 | if !is_empty { | |
1076 | err.multipart_suggestion( | |
1077 | "you can use the `?` operator instead", | |
1078 | vec![(try_span, "".to_owned()), (hi, "?".to_owned())], | |
1079 | Applicability::MachineApplicable, | |
1080 | ); | |
1081 | } | |
1082 | err.span_suggestion(lo.shrink_to_lo(), &format!("{}you can still access the deprecated `try!()` macro using the \"raw identifier\" syntax", prefix), "r#".to_string(), Applicability::MachineApplicable); | |
1083 | err.emit(); | |
1084 | Ok(self.mk_expr_err(lo.to(hi))) | |
1085 | } else { | |
1086 | Err(self.expected_expression_found()) // The user isn't trying to invoke the try! macro | |
1087 | } | |
1088 | } | |
1089 | ||
e1599b0c | 1090 | /// Recovers a situation like `for ( $pat in $expr )` |
416331ca XL |
1091 | /// and suggest writing `for $pat in $expr` instead. |
1092 | /// | |
1093 | /// This should be called before parsing the `$block`. | |
e74abb32 | 1094 | pub(super) fn recover_parens_around_for_head( |
416331ca XL |
1095 | &mut self, |
1096 | pat: P<Pat>, | |
1097 | expr: &Expr, | |
1098 | begin_paren: Option<Span>, | |
1099 | ) -> P<Pat> { | |
1100 | match (&self.token.kind, begin_paren) { | |
1101 | (token::CloseDelim(token::Paren), Some(begin_par_sp)) => { | |
1102 | self.bump(); | |
1103 | ||
1104 | let pat_str = self | |
1105 | // Remove the `(` from the span of the pattern: | |
1106 | .span_to_snippet(pat.span.trim_start(begin_par_sp).unwrap()) | |
1107 | .unwrap_or_else(|_| pprust::pat_to_string(&pat)); | |
1108 | ||
74b04a01 | 1109 | self.struct_span_err(self.prev_token.span, "unexpected closing `)`") |
416331ca XL |
1110 | .span_label(begin_par_sp, "opening `(`") |
1111 | .span_suggestion( | |
74b04a01 | 1112 | begin_par_sp.to(self.prev_token.span), |
416331ca XL |
1113 | "remove parenthesis in `for` loop", |
1114 | format!("{} in {}", pat_str, pprust::expr_to_string(&expr)), | |
1115 | // With e.g. `for (x) in y)` this would replace `(x) in y)` | |
1116 | // with `x) in y)` which is syntactically invalid. | |
1117 | // However, this is prevented before we get here. | |
1118 | Applicability::MachineApplicable, | |
1119 | ) | |
1120 | .emit(); | |
1121 | ||
1122 | // Unwrap `(pat)` into `pat` to avoid the `unused_parens` lint. | |
e74abb32 | 1123 | pat.and_then(|pat| match pat.kind { |
416331ca XL |
1124 | PatKind::Paren(pat) => pat, |
1125 | _ => P(pat), | |
1126 | }) | |
1127 | } | |
1128 | _ => pat, | |
1129 | } | |
1130 | } | |
1131 | ||
e74abb32 | 1132 | pub(super) fn could_ascription_be_path(&self, node: &ast::ExprKind) -> bool { |
60c5eb7d | 1133 | (self.token == token::Lt && // `foo:<bar`, likely a typoed turbofish. |
dfeec247 XL |
1134 | self.look_ahead(1, |t| t.is_ident() && !t.is_reserved_ident())) |
1135 | || self.token.is_ident() && | |
60c5eb7d XL |
1136 | match node { |
1137 | // `foo::` → `foo:` or `foo.bar::` → `foo.bar:` | |
1138 | ast::ExprKind::Path(..) | ast::ExprKind::Field(..) => true, | |
1139 | _ => false, | |
1140 | } && | |
48663c56 | 1141 | !self.token.is_reserved_ident() && // v `foo:bar(baz)` |
dfeec247 XL |
1142 | self.look_ahead(1, |t| t == &token::OpenDelim(token::Paren)) |
1143 | || self.look_ahead(1, |t| t == &token::Lt) && // `foo:bar<baz` | |
1144 | self.look_ahead(2, |t| t.is_ident()) | |
1145 | || self.look_ahead(1, |t| t == &token::Colon) && // `foo:bar:baz` | |
1146 | self.look_ahead(2, |t| t.is_ident()) | |
1147 | || self.look_ahead(1, |t| t == &token::ModSep) | |
1148 | && (self.look_ahead(2, |t| t.is_ident()) || // `foo:bar::baz` | |
ba9703b0 | 1149 | self.look_ahead(2, |t| t == &token::Lt)) // `foo:bar::<baz>` |
48663c56 XL |
1150 | } |
1151 | ||
e74abb32 | 1152 | pub(super) fn recover_seq_parse_error( |
48663c56 XL |
1153 | &mut self, |
1154 | delim: token::DelimToken, | |
1155 | lo: Span, | |
1156 | result: PResult<'a, P<Expr>>, | |
1157 | ) -> P<Expr> { | |
1158 | match result { | |
1159 | Ok(x) => x, | |
1160 | Err(mut err) => { | |
1161 | err.emit(); | |
e74abb32 XL |
1162 | // Recover from parse error, callers expect the closing delim to be consumed. |
1163 | self.consume_block(delim, ConsumeClosingDelim::Yes); | |
74b04a01 | 1164 | self.mk_expr(lo.to(self.prev_token.span), ExprKind::Err, AttrVec::new()) |
48663c56 XL |
1165 | } |
1166 | } | |
1167 | } | |
1168 | ||
e74abb32 | 1169 | pub(super) fn recover_closing_delimiter( |
48663c56 | 1170 | &mut self, |
dc9dc135 | 1171 | tokens: &[TokenKind], |
48663c56 XL |
1172 | mut err: DiagnosticBuilder<'a>, |
1173 | ) -> PResult<'a, bool> { | |
1174 | let mut pos = None; | |
e1599b0c | 1175 | // We want to use the last closing delim that would apply. |
48663c56 XL |
1176 | for (i, unmatched) in self.unclosed_delims.iter().enumerate().rev() { |
1177 | if tokens.contains(&token::CloseDelim(unmatched.expected_delim)) | |
dc9dc135 | 1178 | && Some(self.token.span) > unmatched.unclosed_span |
48663c56 XL |
1179 | { |
1180 | pos = Some(i); | |
1181 | } | |
1182 | } | |
1183 | match pos { | |
1184 | Some(pos) => { | |
1185 | // Recover and assume that the detected unclosed delimiter was meant for | |
1186 | // this location. Emit the diagnostic and act as if the delimiter was | |
1187 | // present for the parser's sake. | |
1188 | ||
dfeec247 | 1189 | // Don't attempt to recover from this unclosed delimiter more than once. |
48663c56 XL |
1190 | let unmatched = self.unclosed_delims.remove(pos); |
1191 | let delim = TokenType::Token(token::CloseDelim(unmatched.expected_delim)); | |
e74abb32 XL |
1192 | if unmatched.found_delim.is_none() { |
1193 | // We encountered `Eof`, set this fact here to avoid complaining about missing | |
1194 | // `fn main()` when we found place to suggest the closing brace. | |
1195 | *self.sess.reached_eof.borrow_mut() = true; | |
1196 | } | |
48663c56 | 1197 | |
e1599b0c | 1198 | // We want to suggest the inclusion of the closing delimiter where it makes |
48663c56 XL |
1199 | // the most sense, which is immediately after the last token: |
1200 | // | |
1201 | // {foo(bar {}} | |
1202 | // - ^ | |
1203 | // | | | |
dc9dc135 | 1204 | // | help: `)` may belong here |
48663c56 XL |
1205 | // | |
1206 | // unclosed delimiter | |
1207 | if let Some(sp) = unmatched.unclosed_span { | |
1208 | err.span_label(sp, "unclosed delimiter"); | |
1209 | } | |
1210 | err.span_suggestion_short( | |
74b04a01 | 1211 | self.prev_token.span.shrink_to_hi(), |
48663c56 XL |
1212 | &format!("{} may belong here", delim.to_string()), |
1213 | delim.to_string(), | |
1214 | Applicability::MaybeIncorrect, | |
1215 | ); | |
e74abb32 XL |
1216 | if unmatched.found_delim.is_none() { |
1217 | // Encountered `Eof` when lexing blocks. Do not recover here to avoid knockdown | |
1218 | // errors which would be emitted elsewhere in the parser and let other error | |
1219 | // recovery consume the rest of the file. | |
1220 | Err(err) | |
1221 | } else { | |
1222 | err.emit(); | |
dfeec247 | 1223 | self.expected_tokens.clear(); // Reduce the number of errors. |
e74abb32 XL |
1224 | Ok(true) |
1225 | } | |
48663c56 XL |
1226 | } |
1227 | _ => Err(err), | |
1228 | } | |
1229 | } | |
1230 | ||
e1599b0c XL |
1231 | /// Eats tokens until we can be relatively sure we reached the end of the |
1232 | /// statement. This is something of a best-effort heuristic. | |
1233 | /// | |
1234 | /// We terminate when we find an unmatched `}` (without consuming it). | |
e74abb32 | 1235 | pub(super) fn recover_stmt(&mut self) { |
48663c56 XL |
1236 | self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore) |
1237 | } | |
1238 | ||
e1599b0c XL |
1239 | /// If `break_on_semi` is `Break`, then we will stop consuming tokens after |
1240 | /// finding (and consuming) a `;` outside of `{}` or `[]` (note that this is | |
1241 | /// approximate -- it can mean we break too early due to macros, but that | |
1242 | /// should only lead to sub-optimal recovery, not inaccurate parsing). | |
1243 | /// | |
1244 | /// If `break_on_block` is `Break`, then we will stop consuming tokens | |
1245 | /// after finding (and consuming) a brace-delimited block. | |
e74abb32 XL |
1246 | pub(super) fn recover_stmt_( |
1247 | &mut self, | |
1248 | break_on_semi: SemiColonMode, | |
1249 | break_on_block: BlockMode, | |
1250 | ) { | |
48663c56 XL |
1251 | let mut brace_depth = 0; |
1252 | let mut bracket_depth = 0; | |
1253 | let mut in_block = false; | |
dfeec247 | 1254 | debug!("recover_stmt_ enter loop (semi={:?}, block={:?})", break_on_semi, break_on_block); |
48663c56 XL |
1255 | loop { |
1256 | debug!("recover_stmt_ loop {:?}", self.token); | |
dc9dc135 | 1257 | match self.token.kind { |
48663c56 XL |
1258 | token::OpenDelim(token::DelimToken::Brace) => { |
1259 | brace_depth += 1; | |
1260 | self.bump(); | |
dfeec247 XL |
1261 | if break_on_block == BlockMode::Break && brace_depth == 1 && bracket_depth == 0 |
1262 | { | |
48663c56 XL |
1263 | in_block = true; |
1264 | } | |
1265 | } | |
1266 | token::OpenDelim(token::DelimToken::Bracket) => { | |
1267 | bracket_depth += 1; | |
1268 | self.bump(); | |
1269 | } | |
1270 | token::CloseDelim(token::DelimToken::Brace) => { | |
1271 | if brace_depth == 0 { | |
1272 | debug!("recover_stmt_ return - close delim {:?}", self.token); | |
1273 | break; | |
1274 | } | |
1275 | brace_depth -= 1; | |
1276 | self.bump(); | |
1277 | if in_block && bracket_depth == 0 && brace_depth == 0 { | |
1278 | debug!("recover_stmt_ return - block end {:?}", self.token); | |
1279 | break; | |
1280 | } | |
1281 | } | |
1282 | token::CloseDelim(token::DelimToken::Bracket) => { | |
1283 | bracket_depth -= 1; | |
1284 | if bracket_depth < 0 { | |
1285 | bracket_depth = 0; | |
1286 | } | |
1287 | self.bump(); | |
1288 | } | |
1289 | token::Eof => { | |
1290 | debug!("recover_stmt_ return - Eof"); | |
1291 | break; | |
1292 | } | |
1293 | token::Semi => { | |
1294 | self.bump(); | |
dfeec247 XL |
1295 | if break_on_semi == SemiColonMode::Break |
1296 | && brace_depth == 0 | |
1297 | && bracket_depth == 0 | |
1298 | { | |
48663c56 XL |
1299 | debug!("recover_stmt_ return - Semi"); |
1300 | break; | |
1301 | } | |
1302 | } | |
dfeec247 XL |
1303 | token::Comma |
1304 | if break_on_semi == SemiColonMode::Comma | |
1305 | && brace_depth == 0 | |
1306 | && bracket_depth == 0 => | |
48663c56 XL |
1307 | { |
1308 | debug!("recover_stmt_ return - Semi"); | |
1309 | break; | |
1310 | } | |
dfeec247 | 1311 | _ => self.bump(), |
48663c56 XL |
1312 | } |
1313 | } | |
1314 | } | |
1315 | ||
e74abb32 | 1316 | pub(super) fn check_for_for_in_in_typo(&mut self, in_span: Span) { |
dc9dc135 XL |
1317 | if self.eat_keyword(kw::In) { |
1318 | // a common typo: `for _ in in bar {}` | |
74b04a01 | 1319 | self.struct_span_err(self.prev_token.span, "expected iterable, found keyword `in`") |
416331ca | 1320 | .span_suggestion_short( |
74b04a01 | 1321 | in_span.until(self.prev_token.span), |
416331ca XL |
1322 | "remove the duplicated `in`", |
1323 | String::new(), | |
1324 | Applicability::MachineApplicable, | |
1325 | ) | |
1326 | .emit(); | |
dc9dc135 XL |
1327 | } |
1328 | } | |
1329 | ||
e74abb32 | 1330 | pub(super) fn expected_semi_or_open_brace<T>(&mut self) -> PResult<'a, T> { |
dfeec247 XL |
1331 | let token_str = super::token_descr(&self.token); |
1332 | let msg = &format!("expected `;` or `{{`, found {}", token_str); | |
1333 | let mut err = self.struct_span_err(self.token.span, msg); | |
dc9dc135 XL |
1334 | err.span_label(self.token.span, "expected `;` or `{`"); |
1335 | Err(err) | |
1336 | } | |
1337 | ||
e74abb32 | 1338 | pub(super) fn eat_incorrect_doc_comment_for_param_type(&mut self) { |
dc9dc135 | 1339 | if let token::DocComment(_) = self.token.kind { |
416331ca | 1340 | self.struct_span_err( |
dc9dc135 XL |
1341 | self.token.span, |
1342 | "documentation comments cannot be applied to a function parameter's type", | |
416331ca XL |
1343 | ) |
1344 | .span_label(self.token.span, "doc comments are not allowed here") | |
1345 | .emit(); | |
dc9dc135 | 1346 | self.bump(); |
dfeec247 XL |
1347 | } else if self.token == token::Pound |
1348 | && self.look_ahead(1, |t| *t == token::OpenDelim(token::Bracket)) | |
1349 | { | |
dc9dc135 XL |
1350 | let lo = self.token.span; |
1351 | // Skip every token until next possible arg. | |
1352 | while self.token != token::CloseDelim(token::Bracket) { | |
1353 | self.bump(); | |
1354 | } | |
1355 | let sp = lo.to(self.token.span); | |
1356 | self.bump(); | |
dfeec247 XL |
1357 | self.struct_span_err(sp, "attributes cannot be applied to a function parameter's type") |
1358 | .span_label(sp, "attributes are not allowed here") | |
1359 | .emit(); | |
dc9dc135 XL |
1360 | } |
1361 | } | |
1362 | ||
e74abb32 | 1363 | pub(super) fn parameter_without_type( |
dc9dc135 XL |
1364 | &mut self, |
1365 | err: &mut DiagnosticBuilder<'_>, | |
1366 | pat: P<ast::Pat>, | |
1367 | require_name: bool, | |
74b04a01 | 1368 | first_param: bool, |
dc9dc135 XL |
1369 | ) -> Option<Ident> { |
1370 | // If we find a pattern followed by an identifier, it could be an (incorrect) | |
1371 | // C-style parameter declaration. | |
dfeec247 XL |
1372 | if self.check_ident() |
1373 | && self.look_ahead(1, |t| *t == token::Comma || *t == token::CloseDelim(token::Paren)) | |
1374 | { | |
1375 | // `fn foo(String s) {}` | |
dc9dc135 XL |
1376 | let ident = self.parse_ident().unwrap(); |
1377 | let span = pat.span.with_hi(ident.span.hi()); | |
1378 | ||
1379 | err.span_suggestion( | |
1380 | span, | |
1381 | "declare the type after the parameter binding", | |
1382 | String::from("<identifier>: <type>"), | |
1383 | Applicability::HasPlaceholders, | |
1384 | ); | |
1385 | return Some(ident); | |
e74abb32 | 1386 | } else if let PatKind::Ident(_, ident, _) = pat.kind { |
dfeec247 | 1387 | if require_name |
74b04a01 | 1388 | && (self.token == token::Comma |
dfeec247 XL |
1389 | || self.token == token::Lt |
1390 | || self.token == token::CloseDelim(token::Paren)) | |
1391 | { | |
1392 | // `fn foo(a, b) {}`, `fn foo(a<x>, b<y>) {}` or `fn foo(usize, usize) {}` | |
74b04a01 | 1393 | if first_param { |
e74abb32 XL |
1394 | err.span_suggestion( |
1395 | pat.span, | |
1396 | "if this is a `self` type, give it a parameter name", | |
1397 | format!("self: {}", ident), | |
1398 | Applicability::MaybeIncorrect, | |
1399 | ); | |
1400 | } | |
1401 | // Avoid suggesting that `fn foo(HashMap<u32>)` is fixed with a change to | |
1402 | // `fn foo(HashMap: TypeName<u32>)`. | |
1403 | if self.token != token::Lt { | |
1404 | err.span_suggestion( | |
1405 | pat.span, | |
1406 | "if this was a parameter name, give it a type", | |
1407 | format!("{}: TypeName", ident), | |
1408 | Applicability::HasPlaceholders, | |
1409 | ); | |
1410 | } | |
dc9dc135 XL |
1411 | err.span_suggestion( |
1412 | pat.span, | |
1413 | "if this is a type, explicitly ignore the parameter name", | |
1414 | format!("_: {}", ident), | |
1415 | Applicability::MachineApplicable, | |
1416 | ); | |
1417 | err.note("anonymous parameters are removed in the 2018 edition (see RFC 1685)"); | |
e74abb32 XL |
1418 | |
1419 | // Don't attempt to recover by using the `X` in `X<Y>` as the parameter name. | |
1420 | return if self.token == token::Lt { None } else { Some(ident) }; | |
dc9dc135 XL |
1421 | } |
1422 | } | |
1423 | None | |
1424 | } | |
1425 | ||
e74abb32 | 1426 | pub(super) fn recover_arg_parse(&mut self) -> PResult<'a, (P<ast::Pat>, P<ast::Ty>)> { |
dc9dc135 XL |
1427 | let pat = self.parse_pat(Some("argument name"))?; |
1428 | self.expect(&token::Colon)?; | |
1429 | let ty = self.parse_ty()?; | |
1430 | ||
60c5eb7d XL |
1431 | struct_span_err!( |
1432 | self.diagnostic(), | |
1433 | pat.span, | |
1434 | E0642, | |
1435 | "patterns aren't allowed in methods without bodies", | |
1436 | ) | |
1437 | .span_suggestion_short( | |
1438 | pat.span, | |
1439 | "give this argument a name or use an underscore to ignore it", | |
1440 | "_".to_owned(), | |
1441 | Applicability::MachineApplicable, | |
1442 | ) | |
1443 | .emit(); | |
dc9dc135 XL |
1444 | |
1445 | // Pretend the pattern is `_`, to avoid duplicate errors from AST validation. | |
dfeec247 | 1446 | let pat = P(Pat { kind: PatKind::Wild, span: pat.span, id: ast::DUMMY_NODE_ID }); |
dc9dc135 XL |
1447 | Ok((pat, ty)) |
1448 | } | |
1449 | ||
74b04a01 | 1450 | pub(super) fn recover_bad_self_param(&mut self, mut param: Param) -> PResult<'a, Param> { |
e1599b0c | 1451 | let sp = param.pat.span; |
e74abb32 | 1452 | param.ty.kind = TyKind::Err; |
74b04a01 XL |
1453 | self.struct_span_err(sp, "unexpected `self` parameter in function") |
1454 | .span_label(sp, "must be the first parameter of an associated function") | |
1455 | .emit(); | |
e1599b0c | 1456 | Ok(param) |
dc9dc135 XL |
1457 | } |
1458 | ||
e74abb32 XL |
1459 | pub(super) fn consume_block( |
1460 | &mut self, | |
1461 | delim: token::DelimToken, | |
1462 | consume_close: ConsumeClosingDelim, | |
1463 | ) { | |
48663c56 XL |
1464 | let mut brace_depth = 0; |
1465 | loop { | |
1466 | if self.eat(&token::OpenDelim(delim)) { | |
1467 | brace_depth += 1; | |
e74abb32 | 1468 | } else if self.check(&token::CloseDelim(delim)) { |
48663c56 | 1469 | if brace_depth == 0 { |
e74abb32 XL |
1470 | if let ConsumeClosingDelim::Yes = consume_close { |
1471 | // Some of the callers of this method expect to be able to parse the | |
1472 | // closing delimiter themselves, so we leave it alone. Otherwise we advance | |
1473 | // the parser. | |
1474 | self.bump(); | |
1475 | } | |
48663c56 XL |
1476 | return; |
1477 | } else { | |
e74abb32 | 1478 | self.bump(); |
48663c56 XL |
1479 | brace_depth -= 1; |
1480 | continue; | |
1481 | } | |
1482 | } else if self.token == token::Eof || self.eat(&token::CloseDelim(token::NoDelim)) { | |
1483 | return; | |
1484 | } else { | |
1485 | self.bump(); | |
1486 | } | |
1487 | } | |
1488 | } | |
1489 | ||
e74abb32 | 1490 | pub(super) fn expected_expression_found(&self) -> DiagnosticBuilder<'a> { |
dc9dc135 XL |
1491 | let (span, msg) = match (&self.token.kind, self.subparser_name) { |
1492 | (&token::Eof, Some(origin)) => { | |
1493 | let sp = self.sess.source_map().next_point(self.token.span); | |
1494 | (sp, format!("expected expression, found end of {}", origin)) | |
1495 | } | |
dfeec247 XL |
1496 | _ => ( |
1497 | self.token.span, | |
1498 | format!("expected expression, found {}", super::token_descr(&self.token),), | |
1499 | ), | |
dc9dc135 XL |
1500 | }; |
1501 | let mut err = self.struct_span_err(span, &msg); | |
1502 | let sp = self.sess.source_map().start_point(self.token.span); | |
1503 | if let Some(sp) = self.sess.ambiguous_block_expr_parse.borrow().get(&sp) { | |
1504 | self.sess.expr_parentheses_needed(&mut err, *sp, None); | |
1505 | } | |
1506 | err.span_label(span, "expected expression"); | |
1507 | err | |
1508 | } | |
1509 | ||
e74abb32 XL |
1510 | fn consume_tts( |
1511 | &mut self, | |
1512 | mut acc: i64, // `i64` because malformed code can have more closing delims than opening. | |
1513 | // Not using `FxHashMap` due to `token::TokenKind: !Eq + !Hash`. | |
1514 | modifier: &[(token::TokenKind, i64)], | |
1515 | ) { | |
1516 | while acc > 0 { | |
1517 | if let Some((_, val)) = modifier.iter().find(|(t, _)| *t == self.token.kind) { | |
1518 | acc += *val; | |
1519 | } | |
1520 | if self.token.kind == token::Eof { | |
1521 | break; | |
1522 | } | |
1523 | self.bump(); | |
1524 | } | |
1525 | } | |
1526 | ||
60c5eb7d | 1527 | /// Replace duplicated recovered parameters with `_` pattern to avoid unnecessary errors. |
dc9dc135 XL |
1528 | /// |
1529 | /// This is necessary because at this point we don't know whether we parsed a function with | |
e1599b0c | 1530 | /// anonymous parameters or a function with names but no types. In order to minimize |
60c5eb7d | 1531 | /// unnecessary errors, we assume the parameters are in the shape of `fn foo(a, b, c)` where |
e1599b0c | 1532 | /// the parameters are *names* (so we don't emit errors about not being able to find `b` in |
dc9dc135 | 1533 | /// the local scope), but if we find the same name multiple times, like in `fn foo(i8, i8)`, |
e1599b0c | 1534 | /// we deduplicate them to not complain about duplicated parameter names. |
e74abb32 | 1535 | pub(super) fn deduplicate_recovered_params_names(&self, fn_inputs: &mut Vec<Param>) { |
dc9dc135 XL |
1536 | let mut seen_inputs = FxHashSet::default(); |
1537 | for input in fn_inputs.iter_mut() { | |
dfeec247 XL |
1538 | let opt_ident = if let (PatKind::Ident(_, ident, _), TyKind::Err) = |
1539 | (&input.pat.kind, &input.ty.kind) | |
1540 | { | |
dc9dc135 XL |
1541 | Some(*ident) |
1542 | } else { | |
1543 | None | |
1544 | }; | |
1545 | if let Some(ident) = opt_ident { | |
1546 | if seen_inputs.contains(&ident) { | |
e74abb32 | 1547 | input.pat.kind = PatKind::Wild; |
dc9dc135 XL |
1548 | } |
1549 | seen_inputs.insert(ident); | |
1550 | } | |
1551 | } | |
1552 | } | |
48663c56 | 1553 | } |