Skip to content

Commit 4117c6d

Browse files
committed
Move some parser recovery methods to diagnostics
1 parent 27a2881 commit 4117c6d

File tree

2 files changed

+271
-263
lines changed

2 files changed

+271
-263
lines changed

src/libsyntax/parse/diagnostics.rs

+266-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
use crate::ast;
22
use crate::ast::{BlockCheckMode, Expr, ExprKind, Item, ItemKind, Pat, PatKind, QSelf, Ty, TyKind};
3-
use crate::parse::parser::PathStyle;
3+
use crate::parse::parser::{BlockMode, PathStyle, TokenType, SemiColonMode};
44
use crate::parse::token;
55
use crate::parse::PResult;
66
use crate::parse::Parser;
77
use crate::print::pprust;
88
use crate::ptr::P;
9+
use crate::symbol::keywords;
910
use crate::ThinVec;
10-
use errors::Applicability;
11+
use errors::{Applicability, DiagnosticBuilder};
1112
use syntax_pos::Span;
13+
use log::debug;
1214

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

0 commit comments

Comments
 (0)