|
1 | 1 | use crate::ast;
|
2 |
| -use crate::ast::{Expr, ExprKind, Item, ItemKind, Pat, PatKind, QSelf, Ty, TyKind}; |
3 |
| -use crate::parse::parser::PathStyle; |
| 2 | +use crate::ast::{BlockCheckMode, Expr, ExprKind, Item, ItemKind, Pat, PatKind, QSelf, Ty, TyKind}; |
| 3 | +use crate::parse::parser::{BlockMode, PathStyle, TokenType, SemiColonMode}; |
4 | 4 | use crate::parse::token;
|
5 | 5 | use crate::parse::PResult;
|
6 | 6 | use crate::parse::Parser;
|
7 | 7 | use crate::print::pprust;
|
8 | 8 | use crate::ptr::P;
|
| 9 | +use crate::symbol::keywords; |
9 | 10 | use crate::ThinVec;
|
10 |
| -use errors::Applicability; |
| 11 | +use errors::{Applicability, DiagnosticBuilder}; |
11 | 12 | use syntax_pos::Span;
|
| 13 | +use log::debug; |
12 | 14 |
|
13 | 15 | pub trait RecoverQPath: Sized + 'static {
|
14 | 16 | const PATH_STYLE: PathStyle = PathStyle::Expr;
|
@@ -223,4 +225,300 @@ impl<'a> Parser<'a> {
|
223 | 225 | false
|
224 | 226 | }
|
225 | 227 | }
|
| 228 | + |
| 229 | + /// Consume alternative await syntaxes like `await <expr>`, `await? <expr>`, `await(<expr>)` |
| 230 | + /// and `await { <expr> }`. |
| 231 | + crate fn parse_incorrect_await_syntax( |
| 232 | + &mut self, |
| 233 | + lo: Span, |
| 234 | + await_sp: Span, |
| 235 | + ) -> PResult<'a, (Span, ExprKind)> { |
| 236 | + let is_question = self.eat(&token::Question); // Handle `await? <expr>`. |
| 237 | + let expr = if self.token == token::OpenDelim(token::Brace) { |
| 238 | + // Handle `await { <expr> }`. |
| 239 | + // This needs to be handled separatedly from the next arm to avoid |
| 240 | + // interpreting `await { <expr> }?` as `<expr>?.await`. |
| 241 | + self.parse_block_expr( |
| 242 | + None, |
| 243 | + self.span, |
| 244 | + BlockCheckMode::Default, |
| 245 | + ThinVec::new(), |
| 246 | + ) |
| 247 | + } else { |
| 248 | + self.parse_expr() |
| 249 | + }.map_err(|mut err| { |
| 250 | + err.span_label(await_sp, "while parsing this incorrect await expression"); |
| 251 | + err |
| 252 | + })?; |
| 253 | + let expr_str = self.sess.source_map().span_to_snippet(expr.span) |
| 254 | + .unwrap_or_else(|_| pprust::expr_to_string(&expr)); |
| 255 | + let suggestion = format!("{}.await{}", expr_str, if is_question { "?" } else { "" }); |
| 256 | + let sp = lo.to(expr.span); |
| 257 | + let app = match expr.node { |
| 258 | + ExprKind::Try(_) => Applicability::MaybeIncorrect, // `await <expr>?` |
| 259 | + _ => Applicability::MachineApplicable, |
| 260 | + }; |
| 261 | + self.struct_span_err(sp, "incorrect use of `await`") |
| 262 | + .span_suggestion(sp, "`await` is a postfix operation", suggestion, app) |
| 263 | + .emit(); |
| 264 | + Ok((sp, ExprKind::Await(ast::AwaitOrigin::FieldLike, expr))) |
| 265 | + } |
| 266 | + |
| 267 | + /// If encountering `future.await()`, consume and emit error. |
| 268 | + crate fn recover_from_await_method_call(&mut self) { |
| 269 | + if self.token == token::OpenDelim(token::Paren) && |
| 270 | + self.look_ahead(1, |t| t == &token::CloseDelim(token::Paren)) |
| 271 | + { |
| 272 | + // future.await() |
| 273 | + let lo = self.span; |
| 274 | + self.bump(); // ( |
| 275 | + let sp = lo.to(self.span); |
| 276 | + self.bump(); // ) |
| 277 | + self.struct_span_err(sp, "incorrect use of `await`") |
| 278 | + .span_suggestion( |
| 279 | + sp, |
| 280 | + "`await` is not a method call, remove the parentheses", |
| 281 | + String::new(), |
| 282 | + Applicability::MachineApplicable, |
| 283 | + ).emit() |
| 284 | + } |
| 285 | + } |
| 286 | + |
| 287 | + crate fn could_ascription_be_path(&self, node: &ast::ExprKind) -> bool { |
| 288 | + self.token.is_ident() && |
| 289 | + if let ast::ExprKind::Path(..) = node { true } else { false } && |
| 290 | + !self.token.is_reserved_ident() && // v `foo:bar(baz)` |
| 291 | + self.look_ahead(1, |t| t == &token::OpenDelim(token::Paren)) || |
| 292 | + self.look_ahead(1, |t| t == &token::Lt) && // `foo:bar<baz` |
| 293 | + self.look_ahead(2, |t| t.is_ident()) || |
| 294 | + self.look_ahead(1, |t| t == &token::Colon) && // `foo:bar:baz` |
| 295 | + self.look_ahead(2, |t| t.is_ident()) || |
| 296 | + self.look_ahead(1, |t| t == &token::ModSep) && // `foo:bar::baz` |
| 297 | + self.look_ahead(2, |t| t.is_ident()) |
| 298 | + } |
| 299 | + |
| 300 | + crate fn bad_type_ascription( |
| 301 | + &self, |
| 302 | + err: &mut DiagnosticBuilder<'a>, |
| 303 | + lhs_span: Span, |
| 304 | + cur_op_span: Span, |
| 305 | + next_sp: Span, |
| 306 | + maybe_path: bool, |
| 307 | + ) { |
| 308 | + err.span_label(self.span, "expecting a type here because of type ascription"); |
| 309 | + let cm = self.sess.source_map(); |
| 310 | + let next_pos = cm.lookup_char_pos(next_sp.lo()); |
| 311 | + let op_pos = cm.lookup_char_pos(cur_op_span.hi()); |
| 312 | + if op_pos.line != next_pos.line { |
| 313 | + err.span_suggestion( |
| 314 | + cur_op_span, |
| 315 | + "try using a semicolon", |
| 316 | + ";".to_string(), |
| 317 | + Applicability::MaybeIncorrect, |
| 318 | + ); |
| 319 | + } else { |
| 320 | + if maybe_path { |
| 321 | + err.span_suggestion( |
| 322 | + cur_op_span, |
| 323 | + "maybe you meant to write a path separator here", |
| 324 | + "::".to_string(), |
| 325 | + Applicability::MaybeIncorrect, |
| 326 | + ); |
| 327 | + } else { |
| 328 | + err.note("type ascription is a nightly-only feature that lets \ |
| 329 | + you annotate an expression with a type: `<expr>: <type>`") |
| 330 | + .span_note( |
| 331 | + lhs_span, |
| 332 | + "this expression expects an ascribed type after the colon", |
| 333 | + ) |
| 334 | + .help("this might be indicative of a syntax error elsewhere"); |
| 335 | + } |
| 336 | + } |
| 337 | + } |
| 338 | + |
| 339 | + crate fn recover_seq_parse_error( |
| 340 | + &mut self, |
| 341 | + delim: token::DelimToken, |
| 342 | + lo: Span, |
| 343 | + result: PResult<'a, P<Expr>>, |
| 344 | + ) -> P<Expr> { |
| 345 | + match result { |
| 346 | + Ok(x) => x, |
| 347 | + Err(mut err) => { |
| 348 | + err.emit(); |
| 349 | + // recover from parse error |
| 350 | + self.consume_block(delim); |
| 351 | + self.mk_expr(lo.to(self.prev_span), ExprKind::Err, ThinVec::new()) |
| 352 | + } |
| 353 | + } |
| 354 | + } |
| 355 | + |
| 356 | + crate fn recover_closing_delimiter( |
| 357 | + &mut self, |
| 358 | + tokens: &[token::Token], |
| 359 | + mut err: DiagnosticBuilder<'a>, |
| 360 | + ) -> PResult<'a, bool> { |
| 361 | + let mut pos = None; |
| 362 | + // we want to use the last closing delim that would apply |
| 363 | + for (i, unmatched) in self.unclosed_delims.iter().enumerate().rev() { |
| 364 | + if tokens.contains(&token::CloseDelim(unmatched.expected_delim)) |
| 365 | + && Some(self.span) > unmatched.unclosed_span |
| 366 | + { |
| 367 | + pos = Some(i); |
| 368 | + } |
| 369 | + } |
| 370 | + match pos { |
| 371 | + Some(pos) => { |
| 372 | + // Recover and assume that the detected unclosed delimiter was meant for |
| 373 | + // this location. Emit the diagnostic and act as if the delimiter was |
| 374 | + // present for the parser's sake. |
| 375 | + |
| 376 | + // Don't attempt to recover from this unclosed delimiter more than once. |
| 377 | + let unmatched = self.unclosed_delims.remove(pos); |
| 378 | + let delim = TokenType::Token(token::CloseDelim(unmatched.expected_delim)); |
| 379 | + |
| 380 | + // We want to suggest the inclusion of the closing delimiter where it makes |
| 381 | + // the most sense, which is immediately after the last token: |
| 382 | + // |
| 383 | + // {foo(bar {}} |
| 384 | + // - ^ |
| 385 | + // | | |
| 386 | + // | help: `)` may belong here (FIXME: #58270) |
| 387 | + // | |
| 388 | + // unclosed delimiter |
| 389 | + if let Some(sp) = unmatched.unclosed_span { |
| 390 | + err.span_label(sp, "unclosed delimiter"); |
| 391 | + } |
| 392 | + err.span_suggestion_short( |
| 393 | + self.sess.source_map().next_point(self.prev_span), |
| 394 | + &format!("{} may belong here", delim.to_string()), |
| 395 | + delim.to_string(), |
| 396 | + Applicability::MaybeIncorrect, |
| 397 | + ); |
| 398 | + err.emit(); |
| 399 | + self.expected_tokens.clear(); // reduce errors |
| 400 | + Ok(true) |
| 401 | + } |
| 402 | + _ => Err(err), |
| 403 | + } |
| 404 | + } |
| 405 | + |
| 406 | + /// Recover from `pub` keyword in places where it seems _reasonable_ but isn't valid. |
| 407 | + crate fn eat_bad_pub(&mut self) { |
| 408 | + if self.token.is_keyword(keywords::Pub) { |
| 409 | + match self.parse_visibility(false) { |
| 410 | + Ok(vis) => { |
| 411 | + self.diagnostic() |
| 412 | + .struct_span_err(vis.span, "unnecessary visibility qualifier") |
| 413 | + .span_label(vis.span, "`pub` not permitted here") |
| 414 | + .emit(); |
| 415 | + } |
| 416 | + Err(mut err) => err.emit(), |
| 417 | + } |
| 418 | + } |
| 419 | + } |
| 420 | + |
| 421 | + // Eat tokens until we can be relatively sure we reached the end of the |
| 422 | + // statement. This is something of a best-effort heuristic. |
| 423 | + // |
| 424 | + // We terminate when we find an unmatched `}` (without consuming it). |
| 425 | + crate fn recover_stmt(&mut self) { |
| 426 | + self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore) |
| 427 | + } |
| 428 | + |
| 429 | + // If `break_on_semi` is `Break`, then we will stop consuming tokens after |
| 430 | + // finding (and consuming) a `;` outside of `{}` or `[]` (note that this is |
| 431 | + // approximate - it can mean we break too early due to macros, but that |
| 432 | + // should only lead to sub-optimal recovery, not inaccurate parsing). |
| 433 | + // |
| 434 | + // If `break_on_block` is `Break`, then we will stop consuming tokens |
| 435 | + // after finding (and consuming) a brace-delimited block. |
| 436 | + crate fn recover_stmt_(&mut self, break_on_semi: SemiColonMode, break_on_block: BlockMode) { |
| 437 | + let mut brace_depth = 0; |
| 438 | + let mut bracket_depth = 0; |
| 439 | + let mut in_block = false; |
| 440 | + debug!("recover_stmt_ enter loop (semi={:?}, block={:?})", |
| 441 | + break_on_semi, break_on_block); |
| 442 | + loop { |
| 443 | + debug!("recover_stmt_ loop {:?}", self.token); |
| 444 | + match self.token { |
| 445 | + token::OpenDelim(token::DelimToken::Brace) => { |
| 446 | + brace_depth += 1; |
| 447 | + self.bump(); |
| 448 | + if break_on_block == BlockMode::Break && |
| 449 | + brace_depth == 1 && |
| 450 | + bracket_depth == 0 { |
| 451 | + in_block = true; |
| 452 | + } |
| 453 | + } |
| 454 | + token::OpenDelim(token::DelimToken::Bracket) => { |
| 455 | + bracket_depth += 1; |
| 456 | + self.bump(); |
| 457 | + } |
| 458 | + token::CloseDelim(token::DelimToken::Brace) => { |
| 459 | + if brace_depth == 0 { |
| 460 | + debug!("recover_stmt_ return - close delim {:?}", self.token); |
| 461 | + break; |
| 462 | + } |
| 463 | + brace_depth -= 1; |
| 464 | + self.bump(); |
| 465 | + if in_block && bracket_depth == 0 && brace_depth == 0 { |
| 466 | + debug!("recover_stmt_ return - block end {:?}", self.token); |
| 467 | + break; |
| 468 | + } |
| 469 | + } |
| 470 | + token::CloseDelim(token::DelimToken::Bracket) => { |
| 471 | + bracket_depth -= 1; |
| 472 | + if bracket_depth < 0 { |
| 473 | + bracket_depth = 0; |
| 474 | + } |
| 475 | + self.bump(); |
| 476 | + } |
| 477 | + token::Eof => { |
| 478 | + debug!("recover_stmt_ return - Eof"); |
| 479 | + break; |
| 480 | + } |
| 481 | + token::Semi => { |
| 482 | + self.bump(); |
| 483 | + if break_on_semi == SemiColonMode::Break && |
| 484 | + brace_depth == 0 && |
| 485 | + bracket_depth == 0 { |
| 486 | + debug!("recover_stmt_ return - Semi"); |
| 487 | + break; |
| 488 | + } |
| 489 | + } |
| 490 | + token::Comma if break_on_semi == SemiColonMode::Comma && |
| 491 | + brace_depth == 0 && |
| 492 | + bracket_depth == 0 => |
| 493 | + { |
| 494 | + debug!("recover_stmt_ return - Semi"); |
| 495 | + break; |
| 496 | + } |
| 497 | + _ => { |
| 498 | + self.bump() |
| 499 | + } |
| 500 | + } |
| 501 | + } |
| 502 | + } |
| 503 | + |
| 504 | + crate fn consume_block(&mut self, delim: token::DelimToken) { |
| 505 | + let mut brace_depth = 0; |
| 506 | + loop { |
| 507 | + if self.eat(&token::OpenDelim(delim)) { |
| 508 | + brace_depth += 1; |
| 509 | + } else if self.eat(&token::CloseDelim(delim)) { |
| 510 | + if brace_depth == 0 { |
| 511 | + return; |
| 512 | + } else { |
| 513 | + brace_depth -= 1; |
| 514 | + continue; |
| 515 | + } |
| 516 | + } else if self.token == token::Eof || self.eat(&token::CloseDelim(token::NoDelim)) { |
| 517 | + return; |
| 518 | + } else { |
| 519 | + self.bump(); |
| 520 | + } |
| 521 | + } |
| 522 | + } |
| 523 | + |
226 | 524 | }
|
0 commit comments