use crate::ast;
use crate::ast::{BlockCheckMode, Expr, ExprKind, Item, ItemKind, Pat, PatKind, QSelf, Ty, TyKind};
-use crate::parse::parser::PathStyle;
+use crate::parse::parser::{BlockMode, PathStyle, TokenType, SemiColonMode};
use crate::parse::token;
use crate::parse::PResult;
use crate::parse::Parser;
use crate::print::pprust;
use crate::ptr::P;
+use crate::symbol::keywords;
use crate::ThinVec;
-use errors::Applicability;
+use errors::{Applicability, DiagnosticBuilder};
use syntax_pos::Span;
+use log::debug;
pub trait RecoverQPath: Sized + 'static {
const PATH_STYLE: PathStyle = PathStyle::Expr;
.emit();
Ok((sp, ExprKind::Await(ast::AwaitOrigin::FieldLike, expr)))
}
+
+ /// If encountering `future.await()`, consume and emit error.
+ crate fn recover_from_await_method_call(&mut self) {
+ if self.token == token::OpenDelim(token::Paren) &&
+ self.look_ahead(1, |t| t == &token::CloseDelim(token::Paren))
+ {
+ // future.await()
+ let lo = self.span;
+ self.bump(); // (
+ let sp = lo.to(self.span);
+ self.bump(); // )
+ let mut err = self.struct_span_err(sp, "incorrect use of `await`");
+ err.span_suggestion(
+ sp,
+ "`await` is not a method call, remove the parentheses",
+ String::new(),
+ Applicability::MachineApplicable,
+ );
+ err.emit()
+ }
+ }
+
+ crate fn could_ascription_be_path(&self, node: &ast::ExprKind) -> bool {
+ self.token.is_ident() &&
+ if let ast::ExprKind::Path(..) = node { true } else { false } &&
+ !self.token.is_reserved_ident() && // v `foo:bar(baz)`
+ self.look_ahead(1, |t| t == &token::OpenDelim(token::Paren)) ||
+ self.look_ahead(1, |t| t == &token::Lt) && // `foo:bar<baz`
+ self.look_ahead(2, |t| t.is_ident()) ||
+ self.look_ahead(1, |t| t == &token::Colon) && // `foo:bar:baz`
+ self.look_ahead(2, |t| t.is_ident()) ||
+ self.look_ahead(1, |t| t == &token::ModSep) && // `foo:bar::baz`
+ self.look_ahead(2, |t| t.is_ident())
+ }
+
+ crate fn bad_type_ascription(
+ &self,
+ err: &mut DiagnosticBuilder<'a>,
+ lhs_span: Span,
+ cur_op_span: Span,
+ next_sp: Span,
+ maybe_path: bool,
+ ) {
+ err.span_label(self.span, "expecting a type here because of type ascription");
+ let cm = self.sess.source_map();
+ let next_pos = cm.lookup_char_pos(next_sp.lo());
+ let op_pos = cm.lookup_char_pos(cur_op_span.hi());
+ if op_pos.line != next_pos.line {
+ err.span_suggestion(
+ cur_op_span,
+ "try using a semicolon",
+ ";".to_string(),
+ Applicability::MaybeIncorrect,
+ );
+ } else {
+ if maybe_path {
+ err.span_suggestion(
+ cur_op_span,
+ "maybe you meant to write a path separator here",
+ "::".to_string(),
+ Applicability::MaybeIncorrect,
+ );
+ } else {
+ err.note("type ascription is a nightly-only feature that lets \
+ you annotate an expression with a type: `<expr>: <type>`");
+ err.span_note(
+ lhs_span,
+ "this expression expects an ascribed type after the colon",
+ );
+ err.help("this might be indicative of a syntax error elsewhere");
+ }
+ }
+ }
+
+ crate fn recover_seq_parse_error(
+ &mut self,
+ delim: token::DelimToken,
+ lo: Span,
+ result: PResult<'a, P<Expr>>,
+ ) -> P<Expr> {
+ match result {
+ Ok(x) => x,
+ Err(mut err) => {
+ err.emit();
+ // recover from parse error
+ self.consume_block(delim);
+ self.mk_expr(lo.to(self.prev_span), ExprKind::Err, ThinVec::new())
+ }
+ }
+ }
+
+ crate fn recover_closing_delimiter(
+ &mut self,
+ tokens: &[token::Token],
+ mut err: DiagnosticBuilder<'a>,
+ ) -> PResult<'a, bool> {
+ let mut pos = None;
+ // we want to use the last closing delim that would apply
+ for (i, unmatched) in self.unclosed_delims.iter().enumerate().rev() {
+ if tokens.contains(&token::CloseDelim(unmatched.expected_delim))
+ && Some(self.span) > unmatched.unclosed_span
+ {
+ pos = Some(i);
+ }
+ }
+ match pos {
+ Some(pos) => {
+ // Recover and assume that the detected unclosed delimiter was meant for
+ // this location. Emit the diagnostic and act as if the delimiter was
+ // present for the parser's sake.
+
+ // Don't attempt to recover from this unclosed delimiter more than once.
+ let unmatched = self.unclosed_delims.remove(pos);
+ let delim = TokenType::Token(token::CloseDelim(unmatched.expected_delim));
+
+ // We want to suggest the inclusion of the closing delimiter where it makes
+ // the most sense, which is immediately after the last token:
+ //
+ // {foo(bar {}}
+ // - ^
+ // | |
+ // | help: `)` may belong here (FIXME: #58270)
+ // |
+ // unclosed delimiter
+ if let Some(sp) = unmatched.unclosed_span {
+ err.span_label(sp, "unclosed delimiter");
+ }
+ err.span_suggestion_short(
+ self.sess.source_map().next_point(self.prev_span),
+ &format!("{} may belong here", delim.to_string()),
+ delim.to_string(),
+ Applicability::MaybeIncorrect,
+ );
+ err.emit();
+ self.expected_tokens.clear(); // reduce errors
+ Ok(true)
+ }
+ _ => Err(err),
+ }
+ }
+
+ /// Recover from `pub` keyword in places where it seems _reasonable_ but isn't valid.
+ crate fn eat_bad_pub(&mut self) {
+ if self.token.is_keyword(keywords::Pub) {
+ match self.parse_visibility(false) {
+ Ok(vis) => {
+ let mut err = self.diagnostic()
+ .struct_span_err(vis.span, "unnecessary visibility qualifier");
+ err.span_label(vis.span, "`pub` not permitted here");
+ err.emit();
+ }
+ Err(mut err) => err.emit(),
+ }
+ }
+ }
+
+ // Eat tokens until we can be relatively sure we reached the end of the
+ // statement. This is something of a best-effort heuristic.
+ //
+ // We terminate when we find an unmatched `}` (without consuming it).
+ crate fn recover_stmt(&mut self) {
+ self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore)
+ }
+
+ // If `break_on_semi` is `Break`, then we will stop consuming tokens after
+ // finding (and consuming) a `;` outside of `{}` or `[]` (note that this is
+ // approximate - it can mean we break too early due to macros, but that
+ // should only lead to sub-optimal recovery, not inaccurate parsing).
+ //
+ // If `break_on_block` is `Break`, then we will stop consuming tokens
+ // after finding (and consuming) a brace-delimited block.
+ crate fn recover_stmt_(&mut self, break_on_semi: SemiColonMode, break_on_block: BlockMode) {
+ let mut brace_depth = 0;
+ let mut bracket_depth = 0;
+ let mut in_block = false;
+ debug!("recover_stmt_ enter loop (semi={:?}, block={:?})",
+ break_on_semi, break_on_block);
+ loop {
+ debug!("recover_stmt_ loop {:?}", self.token);
+ match self.token {
+ token::OpenDelim(token::DelimToken::Brace) => {
+ brace_depth += 1;
+ self.bump();
+ if break_on_block == BlockMode::Break &&
+ brace_depth == 1 &&
+ bracket_depth == 0 {
+ in_block = true;
+ }
+ }
+ token::OpenDelim(token::DelimToken::Bracket) => {
+ bracket_depth += 1;
+ self.bump();
+ }
+ token::CloseDelim(token::DelimToken::Brace) => {
+ if brace_depth == 0 {
+ debug!("recover_stmt_ return - close delim {:?}", self.token);
+ break;
+ }
+ brace_depth -= 1;
+ self.bump();
+ if in_block && bracket_depth == 0 && brace_depth == 0 {
+ debug!("recover_stmt_ return - block end {:?}", self.token);
+ break;
+ }
+ }
+ token::CloseDelim(token::DelimToken::Bracket) => {
+ bracket_depth -= 1;
+ if bracket_depth < 0 {
+ bracket_depth = 0;
+ }
+ self.bump();
+ }
+ token::Eof => {
+ debug!("recover_stmt_ return - Eof");
+ break;
+ }
+ token::Semi => {
+ self.bump();
+ if break_on_semi == SemiColonMode::Break &&
+ brace_depth == 0 &&
+ bracket_depth == 0 {
+ debug!("recover_stmt_ return - Semi");
+ break;
+ }
+ }
+ token::Comma => {
+ if break_on_semi == SemiColonMode::Comma &&
+ brace_depth == 0 &&
+ bracket_depth == 0 {
+ debug!("recover_stmt_ return - Semi");
+ break;
+ } else {
+ self.bump();
+ }
+ }
+ _ => {
+ self.bump()
+ }
+ }
+ }
+ }
+
+ crate fn consume_block(&mut self, delim: token::DelimToken) {
+ let mut brace_depth = 0;
+ loop {
+ if self.eat(&token::OpenDelim(delim)) {
+ brace_depth += 1;
+ } else if self.eat(&token::CloseDelim(delim)) {
+ if brace_depth == 0 {
+ return;
+ } else {
+ brace_depth -= 1;
+ continue;
+ }
+ } else if self.token == token::Eof || self.eat(&token::CloseDelim(token::NoDelim)) {
+ return;
+ } else {
+ self.bump();
+ }
+ }
+ }
+
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
-enum SemiColonMode {
+crate enum SemiColonMode {
Break,
Ignore,
Comma,
}
#[derive(Clone, Copy, PartialEq, Debug)]
-enum BlockMode {
+crate enum BlockMode {
Break,
Ignore,
}
}
impl TokenType {
- fn to_string(&self) -> String {
+ crate fn to_string(&self) -> String {
match *self {
TokenType::Token(ref t) => format!("`{}`", pprust::token_to_string(t)),
TokenType::Keyword(kw) => format!("`{}`", kw.name()),
}
}
- fn recover_closing_delimiter(
- &mut self,
- tokens: &[token::Token],
- mut err: DiagnosticBuilder<'a>,
- ) -> PResult<'a, bool> {
- let mut pos = None;
- // we want to use the last closing delim that would apply
- for (i, unmatched) in self.unclosed_delims.iter().enumerate().rev() {
- if tokens.contains(&token::CloseDelim(unmatched.expected_delim))
- && Some(self.span) > unmatched.unclosed_span
- {
- pos = Some(i);
- }
- }
- match pos {
- Some(pos) => {
- // Recover and assume that the detected unclosed delimiter was meant for
- // this location. Emit the diagnostic and act as if the delimiter was
- // present for the parser's sake.
-
- // Don't attempt to recover from this unclosed delimiter more than once.
- let unmatched = self.unclosed_delims.remove(pos);
- let delim = TokenType::Token(token::CloseDelim(unmatched.expected_delim));
-
- // We want to suggest the inclusion of the closing delimiter where it makes
- // the most sense, which is immediately after the last token:
- //
- // {foo(bar {}}
- // - ^
- // | |
- // | help: `)` may belong here (FIXME: #58270)
- // |
- // unclosed delimiter
- if let Some(sp) = unmatched.unclosed_span {
- err.span_label(sp, "unclosed delimiter");
- }
- err.span_suggestion_short(
- self.sess.source_map().next_point(self.prev_span),
- &format!("{} may belong here", delim.to_string()),
- delim.to_string(),
- Applicability::MaybeIncorrect,
- );
- err.emit();
- self.expected_tokens.clear(); // reduce errors
- Ok(true)
- }
- _ => Err(err),
- }
- }
-
/// Expect next token to be edible or inedible token. If edible,
/// then consume it; if inedible, then return without consuming
/// anything. Signal a fatal error if next token is unexpected.
})
}
- fn mk_expr(&self, span: Span, node: ExprKind, attrs: ThinVec<Attribute>) -> P<Expr> {
+ crate fn mk_expr(&self, span: Span, node: ExprKind, attrs: ThinVec<Attribute>) -> P<Expr> {
P(Expr { node, span, attrs, id: ast::DUMMY_NODE_ID })
}
ExprKind::Await(ast::AwaitOrigin::FieldLike, self_arg),
ThinVec::new(),
);
- if self.token == token::OpenDelim(token::Paren) &&
- self.look_ahead(1, |t| t == &token::CloseDelim(token::Paren))
- {
- // future.await()
- let lo = self.span;
- self.bump(); // (
- let sp = lo.to(self.span);
- self.bump(); // )
- let mut err = self.struct_span_err(sp, "incorrect use of `await`");
- err.span_suggestion(
- sp,
- "`await` is not a method call, remove the parentheses",
- String::new(),
- Applicability::MachineApplicable,
- );
- err.emit()
- }
+ self.recover_from_await_method_call();
return Ok(await_expr);
}
let segment = self.parse_path_segment(PathStyle::Expr)?;
return Ok(e);
}
- fn recover_seq_parse_error(
- &mut self,
- delim: token::DelimToken,
- lo: Span,
- result: PResult<'a, P<Expr>>,
- ) -> P<Expr> {
- match result {
- Ok(x) => x,
- Err(mut err) => {
- err.emit();
- // recover from parse error
- self.consume_block(delim);
- self.mk_expr(lo.to(self.prev_span), ExprKind::Err, ThinVec::new())
- }
- }
- }
-
crate fn process_potential_macro_variable(&mut self) {
let (token, span) = match self.token {
token::Dollar if self.span.ctxt() != syntax_pos::hygiene::SyntaxContext::empty() &&
Ok(lhs)
}
- fn could_ascription_be_path(&self, node: &ast::ExprKind) -> bool {
- self.token.is_ident() &&
- if let ast::ExprKind::Path(..) = node { true } else { false } &&
- !self.token.is_reserved_ident() && // v `foo:bar(baz)`
- self.look_ahead(1, |t| t == &token::OpenDelim(token::Paren)) ||
- self.look_ahead(1, |t| t == &token::Lt) && // `foo:bar<baz`
- self.look_ahead(2, |t| t.is_ident()) ||
- self.look_ahead(1, |t| t == &token::Colon) && // `foo:bar:baz`
- self.look_ahead(2, |t| t.is_ident()) ||
- self.look_ahead(1, |t| t == &token::ModSep) && // `foo:bar::baz`
- self.look_ahead(2, |t| t.is_ident())
- }
-
- fn bad_type_ascription(
- &self,
- err: &mut DiagnosticBuilder<'a>,
- lhs_span: Span,
- cur_op_span: Span,
- next_sp: Span,
- maybe_path: bool,
- ) {
- err.span_label(self.span, "expecting a type here because of type ascription");
- let cm = self.sess.source_map();
- let next_pos = cm.lookup_char_pos(next_sp.lo());
- let op_pos = cm.lookup_char_pos(cur_op_span.hi());
- if op_pos.line != next_pos.line {
- err.span_suggestion(
- cur_op_span,
- "try using a semicolon",
- ";".to_string(),
- Applicability::MaybeIncorrect,
- );
- } else {
- if maybe_path {
- err.span_suggestion(
- cur_op_span,
- "maybe you meant to write a path separator here",
- "::".to_string(),
- Applicability::MaybeIncorrect,
- );
- } else {
- err.note("type ascription is a nightly-only feature that lets \
- you annotate an expression with a type: `<expr>: <type>`");
- err.span_note(
- lhs_span,
- "this expression expects an ascribed type after the colon",
- );
- err.help("this might be indicative of a syntax error elsewhere");
- }
- }
- }
-
fn parse_assoc_op_cast(&mut self, lhs: P<Expr>, lhs_span: Span,
expr_kind: fn(P<Expr>, P<Ty>) -> ExprKind)
-> PResult<'a, P<Expr>> {
Ok(self.parse_stmt_(true))
}
- // Eat tokens until we can be relatively sure we reached the end of the
- // statement. This is something of a best-effort heuristic.
- //
- // We terminate when we find an unmatched `}` (without consuming it).
- fn recover_stmt(&mut self) {
- self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore)
- }
-
- // If `break_on_semi` is `Break`, then we will stop consuming tokens after
- // finding (and consuming) a `;` outside of `{}` or `[]` (note that this is
- // approximate - it can mean we break too early due to macros, but that
- // should only lead to sub-optimal recovery, not inaccurate parsing).
- //
- // If `break_on_block` is `Break`, then we will stop consuming tokens
- // after finding (and consuming) a brace-delimited block.
- fn recover_stmt_(&mut self, break_on_semi: SemiColonMode, break_on_block: BlockMode) {
- let mut brace_depth = 0;
- let mut bracket_depth = 0;
- let mut in_block = false;
- debug!("recover_stmt_ enter loop (semi={:?}, block={:?})",
- break_on_semi, break_on_block);
- loop {
- debug!("recover_stmt_ loop {:?}", self.token);
- match self.token {
- token::OpenDelim(token::DelimToken::Brace) => {
- brace_depth += 1;
- self.bump();
- if break_on_block == BlockMode::Break &&
- brace_depth == 1 &&
- bracket_depth == 0 {
- in_block = true;
- }
- }
- token::OpenDelim(token::DelimToken::Bracket) => {
- bracket_depth += 1;
- self.bump();
- }
- token::CloseDelim(token::DelimToken::Brace) => {
- if brace_depth == 0 {
- debug!("recover_stmt_ return - close delim {:?}", self.token);
- break;
- }
- brace_depth -= 1;
- self.bump();
- if in_block && bracket_depth == 0 && brace_depth == 0 {
- debug!("recover_stmt_ return - block end {:?}", self.token);
- break;
- }
- }
- token::CloseDelim(token::DelimToken::Bracket) => {
- bracket_depth -= 1;
- if bracket_depth < 0 {
- bracket_depth = 0;
- }
- self.bump();
- }
- token::Eof => {
- debug!("recover_stmt_ return - Eof");
- break;
- }
- token::Semi => {
- self.bump();
- if break_on_semi == SemiColonMode::Break &&
- brace_depth == 0 &&
- bracket_depth == 0 {
- debug!("recover_stmt_ return - Semi");
- break;
- }
- }
- token::Comma => {
- if break_on_semi == SemiColonMode::Comma &&
- brace_depth == 0 &&
- bracket_depth == 0 {
- debug!("recover_stmt_ return - Semi");
- break;
- } else {
- self.bump();
- }
- }
- _ => {
- self.bump()
- }
- }
- }
- }
-
fn parse_stmt_(&mut self, macro_legacy_warnings: bool) -> Option<Stmt> {
self.parse_stmt_without_recovery(macro_legacy_warnings).unwrap_or_else(|mut e| {
e.emit();
Ok((class_name, ItemKind::Union(vdata, generics), None))
}
- fn consume_block(&mut self, delim: token::DelimToken) {
- let mut brace_depth = 0;
- loop {
- if self.eat(&token::OpenDelim(delim)) {
- brace_depth += 1;
- } else if self.eat(&token::CloseDelim(delim)) {
- if brace_depth == 0 {
- return;
- } else {
- brace_depth -= 1;
- continue;
- }
- } else if self.token == token::Eof || self.eat(&token::CloseDelim(token::NoDelim)) {
- return;
- } else {
- self.bump();
- }
- }
- }
-
fn parse_record_struct_body(
&mut self,
) -> PResult<'a, (Vec<StructField>, /* recovered */ bool)> {
).emit();
}
- /// Recover from `pub` keyword in places where it seems _reasonable_ but isn't valid.
- fn eat_bad_pub(&mut self) {
- if self.token.is_keyword(keywords::Pub) {
- match self.parse_visibility(false) {
- Ok(vis) => {
- let mut err = self.diagnostic()
- .struct_span_err(vis.span, "unnecessary visibility qualifier");
- err.span_label(vis.span, "`pub` not permitted here");
- err.emit();
- }
- Err(mut err) => err.emit(),
- }
- }
- }
-
/// When lowering a `async fn` to the HIR, we need to move all of the arguments of the function
/// into the generated closure so that they are dropped when the future is polled and not when
/// it is created.