use super::ty::AllowPlus;
use super::{BlockMode, Parser, PathStyle, SemiColonMode, SeqSep, TokenExpectType, TokenType};
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Lit, LitKind, TokenKind};
use rustc_ast::util::parser::AssocOp;
use rustc_ast::{
self as ast, AngleBracketedArgs, AttrVec, BinOpKind, BindingMode, BlockCheckMode, Expr,
ExprKind, Item, ItemKind, Mutability, Param, Pat, PatKind, PathSegment, QSelf, Ty, TyKind,
};
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{pluralize, struct_span_err};
use rustc_errors::{Applicability, DiagnosticBuilder, Handler, PResult};
use rustc_span::source_map::Spanned;
use rustc_span::symbol::{kw, Ident};
use rustc_span::{MultiSpan, Span, SpanSnippetError, DUMMY_SP};
use tracing::{debug, trace};
const TURBOFISH: &str = "use `::<...>` instead of `<...>` to specify type arguments";
pub(super) fn dummy_arg(ident: Ident) -> Param {
let pat = P(Pat {
id: ast::DUMMY_NODE_ID,
kind: PatKind::Ident(BindingMode::ByValue(Mutability::Not), ident, None),
span: ident.span,
tokens: None,
});
let ty = Ty { kind: TyKind::Err, span: ident.span, id: ast::DUMMY_NODE_ID };
Param {
attrs: AttrVec::default(),
id: ast::DUMMY_NODE_ID,
pat,
span: ident.span,
ty: P(ty),
is_placeholder: false,
}
}
pub enum Error {
UselessDocComment,
}
impl Error {
fn span_err(self, sp: impl Into<MultiSpan>, handler: &Handler) -> DiagnosticBuilder<'_> {
match self {
Error::UselessDocComment => {
let mut err = struct_span_err!(
handler,
sp,
E0585,
"found a documentation comment that doesn't document anything",
);
err.help(
"doc comments must come before what they document, maybe a comment was \
intended with `//`?",
);
err
}
}
}
}
pub(super) trait RecoverQPath: Sized + 'static {
const PATH_STYLE: PathStyle = PathStyle::Expr;
fn to_ty(&self) -> Option<P<Ty>>;
fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self;
}
impl RecoverQPath for Ty {
const PATH_STYLE: PathStyle = PathStyle::Type;
fn to_ty(&self) -> Option<P<Ty>> {
Some(P(self.clone()))
}
fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self {
Self { span: path.span, kind: TyKind::Path(qself, path), id: ast::DUMMY_NODE_ID }
}
}
impl RecoverQPath for Pat {
fn to_ty(&self) -> Option<P<Ty>> {
self.to_ty()
}
fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self {
Self {
span: path.span,
kind: PatKind::Path(qself, path),
id: ast::DUMMY_NODE_ID,
tokens: None,
}
}
}
impl RecoverQPath for Expr {
fn to_ty(&self) -> Option<P<Ty>> {
self.to_ty()
}
fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self {
Self {
span: path.span,
kind: ExprKind::Path(qself, path),
attrs: AttrVec::new(),
id: ast::DUMMY_NODE_ID,
tokens: None,
}
}
}
crate enum ConsumeClosingDelim {
Yes,
No,
}
impl<'a> Parser<'a> {
pub(super) fn span_fatal_err<S: Into<MultiSpan>>(
&self,
sp: S,
err: Error,
) -> DiagnosticBuilder<'a> {
err.span_err(sp, self.diagnostic())
}
pub fn struct_span_err<S: Into<MultiSpan>>(&self, sp: S, m: &str) -> DiagnosticBuilder<'a> {
self.sess.span_diagnostic.struct_span_err(sp, m)
}
pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, m: &str) -> ! {
self.sess.span_diagnostic.span_bug(sp, m)
}
pub(super) fn diagnostic(&self) -> &'a Handler {
&self.sess.span_diagnostic
}
pub(super) fn span_to_snippet(&self, span: Span) -> Result<String, SpanSnippetError> {
self.sess.source_map().span_to_snippet(span)
}
pub(super) fn expected_ident_found(&self) -> DiagnosticBuilder<'a> {
let mut err = self.struct_span_err(
self.token.span,
&format!("expected identifier, found {}", super::token_descr(&self.token)),
);
let valid_follow = &[
TokenKind::Eq,
TokenKind::Colon,
TokenKind::Comma,
TokenKind::Semi,
TokenKind::ModSep,
TokenKind::OpenDelim(token::DelimToken::Brace),
TokenKind::OpenDelim(token::DelimToken::Paren),
TokenKind::CloseDelim(token::DelimToken::Brace),
TokenKind::CloseDelim(token::DelimToken::Paren),
];
match self.token.ident() {
Some((ident, false))
if ident.is_raw_guess()
&& self.look_ahead(1, |t| valid_follow.contains(&t.kind)) =>
{
err.span_suggestion(
ident.span,
"you can escape reserved keywords to use them as identifiers",
format!("r#{}", ident.name),
Applicability::MaybeIncorrect,
);
}
_ => {}
}
if let Some(token_descr) = super::token_descr_opt(&self.token) {
err.span_label(self.token.span, format!("expected identifier, found {}", token_descr));
} else {
err.span_label(self.token.span, "expected identifier");
if self.token == token::Comma && self.look_ahead(1, |t| t.is_ident()) {
err.span_suggestion(
self.token.span,
"remove this comma",
String::new(),
Applicability::MachineApplicable,
);
}
}
err
}
pub(super) fn expected_one_of_not_found(
&mut self,
edible: &[TokenKind],
inedible: &[TokenKind],
) -> PResult<'a, bool > {
fn tokens_to_string(tokens: &[TokenType]) -> String {
let mut i = tokens.iter();
let b = i.next().map_or(String::new(), |t| t.to_string());
i.enumerate().fold(b, |mut b, (i, a)| {
if tokens.len() > 2 && i == tokens.len() - 2 {
b.push_str(", or ");
} else if tokens.len() == 2 && i == tokens.len() - 2 {
b.push_str(" or ");
} else {
b.push_str(", ");
}
b.push_str(&a.to_string());
b
})
}
let mut expected = edible
.iter()
.map(|x| TokenType::Token(x.clone()))
.chain(inedible.iter().map(|x| TokenType::Token(x.clone())))
.chain(self.expected_tokens.iter().cloned())
.collect::<Vec<_>>();
expected.sort_by_cached_key(|x| x.to_string());
expected.dedup();
let expect = tokens_to_string(&expected[..]);
let actual = super::token_descr(&self.token);
let (msg_exp, (label_sp, label_exp)) = if expected.len() > 1 {
let short_expect = if expected.len() > 6 {
format!("{} possible tokens", expected.len())
} else {
expect.clone()
};
(
format!("expected one of {}, found {}", expect, actual),
(self.prev_token.span.shrink_to_hi(), format!("expected one of {}", short_expect)),
)
} else if expected.is_empty() {
(
format!("unexpected token: {}", actual),
(self.prev_token.span, "unexpected token after this".to_string()),
)
} else {
(
format!("expected {}, found {}", expect, actual),
(self.prev_token.span.shrink_to_hi(), format!("expected {}", expect)),
)
};
self.last_unexpected_token_span = Some(self.token.span);
let mut err = self.struct_span_err(self.token.span, &msg_exp);
let sp = if self.token == token::Eof {
self.prev_token.span
} else {
label_sp
};
match self.recover_closing_delimiter(
&expected
.iter()
.filter_map(|tt| match tt {
TokenType::Token(t) => Some(t.clone()),
_ => None,
})
.collect::<Vec<_>>(),
err,
) {
Err(e) => err = e,
Ok(recovered) => {
return Ok(recovered);
}
}
if self.check_too_many_raw_str_terminators(&mut err) {
return Err(err);
}
let sm = self.sess.source_map();
if self.prev_token.span == DUMMY_SP {
err.span_label(self.token.span, label_exp);
} else if !sm.is_multiline(self.token.span.shrink_to_hi().until(sp.shrink_to_lo())) {
err.span_label(self.token.span, label_exp);
} else {
err.span_label(sp, label_exp);
err.span_label(self.token.span, "unexpected token");
}
self.maybe_annotate_with_ascription(&mut err, false);
Err(err)
}
fn check_too_many_raw_str_terminators(&mut self, err: &mut DiagnosticBuilder<'_>) -> bool {
match (&self.prev_token.kind, &self.token.kind) {
(
TokenKind::Literal(Lit {
kind: LitKind::StrRaw(n_hashes) | LitKind::ByteStrRaw(n_hashes),
..
}),
TokenKind::Pound,
) => {
err.set_primary_message("too many `#` when terminating raw string");
err.span_suggestion(
self.token.span,
"remove the extra `#`",
String::new(),
Applicability::MachineApplicable,
);
err.note(&format!("the raw string started with {} `#`s", n_hashes));
true
}
_ => false,
}
}
pub fn maybe_annotate_with_ascription(
&mut self,
err: &mut DiagnosticBuilder<'_>,
maybe_expected_semicolon: bool,
) {
if let Some((sp, likely_path)) = self.last_type_ascription.take() {
let sm = self.sess.source_map();
let next_pos = sm.lookup_char_pos(self.token.span.lo());
let op_pos = sm.lookup_char_pos(sp.hi());
let allow_unstable = self.sess.unstable_features.is_nightly_build();
if likely_path {
err.span_suggestion(
sp,
"maybe write a path separator here",
"::".to_string(),
if allow_unstable {
Applicability::MaybeIncorrect
} else {
Applicability::MachineApplicable
},
);
self.sess.type_ascription_path_suggestions.borrow_mut().insert(sp);
} else if op_pos.line != next_pos.line && maybe_expected_semicolon {
err.span_suggestion(
sp,
"try using a semicolon",
";".to_string(),
Applicability::MaybeIncorrect,
);
} else if allow_unstable {
err.span_label(sp, "tried to parse a type due to this type ascription");
} else {
err.span_label(sp, "tried to parse a type due to this");
}
if allow_unstable {
err.note(
"`#![feature(type_ascription)]` lets you annotate an expression with a type: \
`<expr>: <type>`",
);
if !likely_path {
err.note(
"see issue #23416 <https://github.com/rust-lang/rust/issues/23416> \
for more information",
);
}
}
}
}
pub(super) fn eat_to_tokens(&mut self, kets: &[&TokenKind]) {
if let Err(ref mut err) =
self.parse_seq_to_before_tokens(kets, SeqSep::none(), TokenExpectType::Expect, |p| {
Ok(p.parse_token_tree())
})
{
err.cancel();
}
}
pub(super) fn check_trailing_angle_brackets(
&mut self,
segment: &PathSegment,
end: &[&TokenKind],
) -> bool {
let parsed_angle_bracket_args =
segment.args.as_ref().map(|args| args.is_angle_bracketed()).unwrap_or(false);
debug!(
"check_trailing_angle_brackets: parsed_angle_bracket_args={:?}",
parsed_angle_bracket_args,
);
if !parsed_angle_bracket_args {
return false;
}
let lo = self.token.span;
let mut position = 0;
let mut number_of_shr = 0;
let mut number_of_gt = 0;
while self.look_ahead(position, |t| {
trace!("check_trailing_angle_brackets: t={:?}", t);
if *t == token::BinOp(token::BinOpToken::Shr) {
number_of_shr += 1;
true
} else if *t == token::Gt {
number_of_gt += 1;
true
} else {
false
}
}) {
position += 1;
}
debug!(
"check_trailing_angle_brackets: number_of_gt={:?} number_of_shr={:?}",
number_of_gt, number_of_shr,
);
if number_of_gt < 1 && number_of_shr < 1 {
return false;
}
if self.look_ahead(position, |t| {
trace!("check_trailing_angle_brackets: t={:?}", t);
end.contains(&&t.kind)
}) {
self.eat_to_tokens(end);
let span = lo.until(self.token.span);
let total_num_of_gt = number_of_gt + number_of_shr * 2;
self.struct_span_err(
span,
&format!("unmatched angle bracket{}", pluralize!(total_num_of_gt)),
)
.span_suggestion(
span,
&format!("remove extra angle bracket{}", pluralize!(total_num_of_gt)),
String::new(),
Applicability::MachineApplicable,
)
.emit();
return true;
}
false
}
pub(super) fn check_turbofish_missing_angle_brackets(&mut self, segment: &mut PathSegment) {
if token::ModSep == self.token.kind && segment.args.is_none() {
let snapshot = self.clone();
self.bump();
let lo = self.token.span;
match self.parse_angle_args() {
Ok(args) => {
let span = lo.to(self.prev_token.span);
let mut trailing_span = self.prev_token.span.shrink_to_hi();
while self.token.kind == token::BinOp(token::Shr)
|| self.token.kind == token::Gt
{
trailing_span = trailing_span.to(self.token.span);
self.bump();
}
if self.token.kind == token::OpenDelim(token::Paren) {
let args = AngleBracketedArgs { args, span }.into();
segment.args = args;
self.struct_span_err(
span,
"generic parameters without surrounding angle brackets",
)
.multipart_suggestion(
"surround the type parameters with angle brackets",
vec![
(span.shrink_to_lo(), "<".to_string()),
(trailing_span, ">".to_string()),
],
Applicability::MachineApplicable,
)
.emit();
} else {
*self = snapshot;
}
}
Err(mut err) => {
err.cancel();
*self = snapshot;
}
}
}
}
fn attempt_chained_comparison_suggestion(
&mut self,
err: &mut DiagnosticBuilder<'_>,
inner_op: &Expr,
outer_op: &Spanned<AssocOp>,
) -> bool {
if let ExprKind::Binary(op, ref l1, ref r1) = inner_op.kind {
if let ExprKind::Field(_, ident) = l1.kind {
if ident.as_str().parse::<i32>().is_err() && !matches!(r1.kind, ExprKind::Lit(_)) {
return false;
}
}
let mut enclose = |left: Span, right: Span| {
err.multipart_suggestion(
"parenthesize the comparison",
vec![
(left.shrink_to_lo(), "(".to_string()),
(right.shrink_to_hi(), ")".to_string()),
],
Applicability::MaybeIncorrect,
);
};
return match (op.node, &outer_op.node) {
(BinOpKind::Eq, AssocOp::Equal) |
(BinOpKind::Lt, AssocOp::Less | AssocOp::LessEqual) |
(BinOpKind::Le, AssocOp::LessEqual | AssocOp::Less) |
(BinOpKind::Gt, AssocOp::Greater | AssocOp::GreaterEqual) |
(BinOpKind::Ge, AssocOp::GreaterEqual | AssocOp::Greater) => {
let expr_to_str = |e: &Expr| {
self.span_to_snippet(e.span)
.unwrap_or_else(|_| pprust::expr_to_string(&e))
};
err.span_suggestion_verbose(
inner_op.span.shrink_to_hi(),
"split the comparison into two",
format!(" && {}", expr_to_str(&r1)),
Applicability::MaybeIncorrect,
);
false
}
(BinOpKind::Eq, AssocOp::Less | AssocOp::LessEqual | AssocOp::Greater | AssocOp::GreaterEqual) => {
let snapshot = self.clone();
match self.parse_expr() {
Ok(r2) => {
enclose(r1.span, r2.span);
true
}
Err(mut expr_err) => {
expr_err.cancel();
*self = snapshot;
false
}
}
}
(BinOpKind::Lt | BinOpKind::Le | BinOpKind::Gt | BinOpKind::Ge, AssocOp::Equal) => {
let snapshot = self.clone();
match self.parse_expr() {
Ok(_) => {
enclose(l1.span, r1.span);
true
}
Err(mut expr_err) => {
expr_err.cancel();
*self = snapshot;
false
}
}
}
_ => false,
};
}
false
}
pub(super) fn check_no_chained_comparison(
&mut self,
inner_op: &Expr,
outer_op: &Spanned<AssocOp>,
) -> PResult<'a, Option<P<Expr>>> {
debug_assert!(
outer_op.node.is_comparison(),
"check_no_chained_comparison: {:?} is not comparison",
outer_op.node,
);
let mk_err_expr =
|this: &Self, span| Ok(Some(this.mk_expr(span, ExprKind::Err, AttrVec::new())));
match inner_op.kind {
ExprKind::Binary(op, ref l1, ref r1) if op.node.is_comparison() => {
let mut err = self.struct_span_err(
vec![op.span, self.prev_token.span],
"comparison operators cannot be chained",
);
let suggest = |err: &mut DiagnosticBuilder<'_>| {
err.span_suggestion_verbose(
op.span.shrink_to_lo(),
TURBOFISH,
"::".to_string(),
Applicability::MaybeIncorrect,
);
};
if op.node == BinOpKind::Lt && outer_op.node == AssocOp::Less
|| outer_op.node == AssocOp::Greater
{
if outer_op.node == AssocOp::Less {
let snapshot = self.clone();
self.bump();
let modifiers =
[(token::Lt, 1), (token::Gt, -1), (token::BinOp(token::Shr), -2)];
self.consume_tts(1, &modifiers[..]);
if !&[token::OpenDelim(token::Paren), token::ModSep]
.contains(&self.token.kind)
{
*self = snapshot.clone();
}
}
return if token::ModSep == self.token.kind {
suggest(&mut err);
let snapshot = self.clone();
self.bump();
match self.parse_expr() {
Ok(_) => {
err.emit();
mk_err_expr(self, inner_op.span.to(self.prev_token.span))
}
Err(mut expr_err) => {
expr_err.cancel();
*self = snapshot;
Err(err)
}
}
} else if token::OpenDelim(token::Paren) == self.token.kind {
suggest(&mut err);
match self.consume_fn_args() {
Err(()) => Err(err),
Ok(()) => {
err.emit();
mk_err_expr(self, inner_op.span.to(self.prev_token.span))
}
}
} else {
if !matches!(l1.kind, ExprKind::Lit(_))
&& !matches!(r1.kind, ExprKind::Lit(_))
{
err.help(TURBOFISH);
err.help("or use `(...)` if you meant to specify fn arguments");
}
if self.attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op)
{
err.emit();
mk_err_expr(self, inner_op.span.to(self.prev_token.span))
} else {
Err(err)
}
};
}
let recover =
self.attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op);
err.emit();
if recover {
return mk_err_expr(self, inner_op.span.to(self.prev_token.span));
}
}
_ => {}
}
Ok(None)
}
fn consume_fn_args(&mut self) -> Result<(), ()> {
let snapshot = self.clone();
self.bump();
let modifiers =
[(token::OpenDelim(token::Paren), 1), (token::CloseDelim(token::Paren), -1)];
self.consume_tts(1, &modifiers[..]);
if self.token.kind == token::Eof {
*self = snapshot;
Err(())
} else {
Ok(())
}
}
pub(super) fn maybe_report_ambiguous_plus(
&mut self,
allow_plus: AllowPlus,
impl_dyn_multi: bool,
ty: &Ty,
) {
if matches!(allow_plus, AllowPlus::No) && impl_dyn_multi {
let sum_with_parens = format!("({})", pprust::ty_to_string(&ty));
self.struct_span_err(ty.span, "ambiguous `+` in a type")
.span_suggestion(
ty.span,
"use parentheses to disambiguate",
sum_with_parens,
Applicability::MachineApplicable,
)
.emit();
}
}
pub(super) fn maybe_recover_from_bad_type_plus(
&mut self,
allow_plus: AllowPlus,
ty: &Ty,
) -> PResult<'a, ()> {
if matches!(allow_plus, AllowPlus::No) || !self.token.is_like_plus() {
return Ok(());
}
self.bump();
let bounds = self.parse_generic_bounds(None)?;
let sum_span = ty.span.to(self.prev_token.span);
let mut err = struct_span_err!(
self.sess.span_diagnostic,
sum_span,
E0178,
"expected a path on the left-hand side of `+`, not `{}`",
pprust::ty_to_string(ty)
);
match ty.kind {
TyKind::Rptr(ref lifetime, ref mut_ty) => {
let sum_with_parens = pprust::to_string(|s| {
s.s.word("&");
s.print_opt_lifetime(lifetime);
s.print_mutability(mut_ty.mutbl, false);
s.popen();
s.print_type(&mut_ty.ty);
s.print_type_bounds(" +", &bounds);
s.pclose()
});
err.span_suggestion(
sum_span,
"try adding parentheses",
sum_with_parens,
Applicability::MachineApplicable,
);
}
TyKind::Ptr(..) | TyKind::BareFn(..) => {
err.span_label(sum_span, "perhaps you forgot parentheses?");
}
_ => {
err.span_label(sum_span, "expected a path");
}
}
err.emit();
Ok(())
}
pub(super) fn maybe_recover_from_bad_qpath<T: RecoverQPath>(
&mut self,
base: P<T>,
allow_recovery: bool,
) -> PResult<'a, P<T>> {
if allow_recovery && self.token == token::ModSep {
if let Some(ty) = base.to_ty() {
return self.maybe_recover_from_bad_qpath_stage_2(ty.span, ty);
}
}
Ok(base)
}
pub(super) fn maybe_recover_from_bad_qpath_stage_2<T: RecoverQPath>(
&mut self,
ty_span: Span,
ty: P<Ty>,
) -> PResult<'a, P<T>> {
self.expect(&token::ModSep)?;
let mut path = ast::Path { segments: Vec::new(), span: DUMMY_SP };
self.parse_path_segments(&mut path.segments, T::PATH_STYLE)?;
path.span = ty_span.to(self.prev_token.span);
let ty_str = self.span_to_snippet(ty_span).unwrap_or_else(|_| pprust::ty_to_string(&ty));
self.struct_span_err(path.span, "missing angle brackets in associated item path")
.span_suggestion(
path.span,
"try",
format!("<{}>::{}", ty_str, pprust::path_to_string(&path)),
Applicability::MaybeIncorrect,
)
.emit();
let path_span = ty_span.shrink_to_hi();
Ok(P(T::recovered(Some(QSelf { ty, path_span, position: 0 }), path)))
}
pub(super) fn maybe_consume_incorrect_semicolon(&mut self, items: &[P<Item>]) -> bool {
if self.eat(&token::Semi) {
let mut err = self.struct_span_err(self.prev_token.span, "expected item, found `;`");
err.span_suggestion_short(
self.prev_token.span,
"remove this semicolon",
String::new(),
Applicability::MachineApplicable,
);
if !items.is_empty() {
let previous_item = &items[items.len() - 1];
let previous_item_kind_name = match previous_item.kind {
ItemKind::Struct(..) => Some("braced struct"),
ItemKind::Enum(..) => Some("enum"),
ItemKind::Trait(..) => Some("trait"),
ItemKind::Union(..) => Some("union"),
_ => None,
};
if let Some(name) = previous_item_kind_name {
err.help(&format!("{} declarations are not followed by a semicolon", name));
}
}
err.emit();
true
} else {
false
}
}
pub(super) fn unexpected_try_recover(
&mut self,
t: &TokenKind,
) -> PResult<'a, bool > {
let token_str = pprust::token_kind_to_string(t);
let this_token_str = super::token_descr(&self.token);
let (prev_sp, sp) = match (&self.token.kind, self.subparser_name) {
(token::Eof, Some(_)) => {
let sp = self.sess.source_map().next_point(self.token.span);
(sp, sp)
}
_ if self.prev_token.span == DUMMY_SP => (self.token.span, self.token.span),
(token::Eof, None) => (self.prev_token.span, self.token.span),
_ => (self.prev_token.span.shrink_to_hi(), self.token.span),
};
let msg = format!(
"expected `{}`, found {}",
token_str,
match (&self.token.kind, self.subparser_name) {
(token::Eof, Some(origin)) => format!("end of {}", origin),
_ => this_token_str,
},
);
let mut err = self.struct_span_err(sp, &msg);
let label_exp = format!("expected `{}`", token_str);
match self.recover_closing_delimiter(&[t.clone()], err) {
Err(e) => err = e,
Ok(recovered) => {
return Ok(recovered);
}
}
let sm = self.sess.source_map();
if !sm.is_multiline(prev_sp.until(sp)) {
err.span_label(sp, label_exp);
} else {
err.span_label(prev_sp, label_exp);
err.span_label(sp, "unexpected token");
}
Err(err)
}
pub(super) fn expect_semi(&mut self) -> PResult<'a, ()> {
if self.eat(&token::Semi) {
return Ok(());
}
let sm = self.sess.source_map();
let msg = format!("expected `;`, found {}", super::token_descr(&self.token));
let appl = Applicability::MachineApplicable;
if self.token.span == DUMMY_SP || self.prev_token.span == DUMMY_SP {
return self.expect(&token::Semi).map(drop);
} else if !sm.is_multiline(self.prev_token.span.until(self.token.span)) {
} else if [token::Comma, token::Colon].contains(&self.token.kind)
&& self.prev_token.kind == token::CloseDelim(token::Paren)
{
self.expect(&token::Semi)?;
return Ok(());
} else if self.look_ahead(1, |t| {
t == &token::CloseDelim(token::Brace) || t.can_begin_expr() && t.kind != token::Colon
}) && [token::Comma, token::Colon].contains(&self.token.kind)
{
self.bump();
let sp = self.prev_token.span;
self.struct_span_err(sp, &msg)
.span_suggestion_short(sp, "change this to `;`", ";".to_string(), appl)
.emit();
return Ok(());
} else if self.look_ahead(0, |t| {
t == &token::CloseDelim(token::Brace)
|| (
t.can_begin_expr() && t != &token::Semi && t != &token::Pound
)
}) {
let sp = self.prev_token.span.shrink_to_hi();
self.struct_span_err(sp, &msg)
.span_label(self.token.span, "unexpected token")
.span_suggestion_short(sp, "add `;` here", ";".to_string(), appl)
.emit();
return Ok(());
}
self.expect(&token::Semi).map(drop)
}
pub(super) fn recover_incorrect_await_syntax(
&mut self,
lo: Span,
await_sp: Span,
attrs: AttrVec,
) -> PResult<'a, P<Expr>> {
let (hi, expr, is_question) = if self.token == token::Not {
self.recover_await_macro()?
} else {
self.recover_await_prefix(await_sp)?
};
let sp = self.error_on_incorrect_await(lo, hi, &expr, is_question);
let expr = self.mk_expr(lo.to(sp), ExprKind::Await(expr), attrs);
self.maybe_recover_from_bad_qpath(expr, true)
}
fn recover_await_macro(&mut self) -> PResult<'a, (Span, P<Expr>, bool)> {
self.expect(&token::Not)?;
self.expect(&token::OpenDelim(token::Paren))?;
let expr = self.parse_expr()?;
self.expect(&token::CloseDelim(token::Paren))?;
Ok((self.prev_token.span, expr, false))
}
fn recover_await_prefix(&mut self, await_sp: Span) -> PResult<'a, (Span, P<Expr>, bool)> {
let is_question = self.eat(&token::Question);
let expr = if self.token == token::OpenDelim(token::Brace) {
self.parse_block_expr(None, self.token.span, BlockCheckMode::Default, AttrVec::new())
} else {
self.parse_expr()
}
.map_err(|mut err| {
err.span_label(await_sp, "while parsing this incorrect await expression");
err
})?;
Ok((expr.span, expr, is_question))
}
fn error_on_incorrect_await(&self, lo: Span, hi: Span, expr: &Expr, is_question: bool) -> Span {
let expr_str =
self.span_to_snippet(expr.span).unwrap_or_else(|_| pprust::expr_to_string(&expr));
let suggestion = format!("{}.await{}", expr_str, if is_question { "?" } else { "" });
let sp = lo.to(hi);
let app = match expr.kind {
ExprKind::Try(_) => Applicability::MaybeIncorrect,
_ => Applicability::MachineApplicable,
};
self.struct_span_err(sp, "incorrect use of `await`")
.span_suggestion(sp, "`await` is a postfix operation", suggestion, app)
.emit();
sp
}
pub(super) 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))
{
let lo = self.token.span;
self.bump();
let sp = lo.to(self.token.span);
self.bump();
self.struct_span_err(sp, "incorrect use of `await`")
.span_suggestion(
sp,
"`await` is not a method call, remove the parentheses",
String::new(),
Applicability::MachineApplicable,
)
.emit();
}
}
pub(super) fn try_macro_suggestion(&mut self) -> PResult<'a, P<Expr>> {
let is_try = self.token.is_keyword(kw::Try);
let is_questionmark = self.look_ahead(1, |t| t == &token::Not);
let is_open = self.look_ahead(2, |t| t == &token::OpenDelim(token::Paren));
if is_try && is_questionmark && is_open {
let lo = self.token.span;
self.bump();
self.bump();
let try_span = lo.to(self.token.span);
self.bump();
let is_empty = self.token == token::CloseDelim(token::Paren);
self.consume_block(token::Paren, ConsumeClosingDelim::No);
let hi = self.token.span;
self.bump();
let mut err = self.struct_span_err(lo.to(hi), "use of deprecated `try` macro");
err.note("in the 2018 edition `try` is a reserved keyword, and the `try!()` macro is deprecated");
let prefix = if is_empty { "" } else { "alternatively, " };
if !is_empty {
err.multipart_suggestion(
"you can use the `?` operator instead",
vec![(try_span, "".to_owned()), (hi, "?".to_owned())],
Applicability::MachineApplicable,
);
}
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);
err.emit();
Ok(self.mk_expr_err(lo.to(hi)))
} else {
Err(self.expected_expression_found())
}
}
pub(super) fn recover_parens_around_for_head(
&mut self,
pat: P<Pat>,
expr: &Expr,
begin_paren: Option<Span>,
) -> P<Pat> {
match (&self.token.kind, begin_paren) {
(token::CloseDelim(token::Paren), Some(begin_par_sp)) => {
self.bump();
let pat_str = self
.span_to_snippet(pat.span.trim_start(begin_par_sp).unwrap())
.unwrap_or_else(|_| pprust::pat_to_string(&pat));
self.struct_span_err(self.prev_token.span, "unexpected closing `)`")
.span_label(begin_par_sp, "opening `(`")
.span_suggestion(
begin_par_sp.to(self.prev_token.span),
"remove parenthesis in `for` loop",
format!("{} in {}", pat_str, pprust::expr_to_string(&expr)),
Applicability::MachineApplicable,
)
.emit();
pat.and_then(|pat| match pat.kind {
PatKind::Paren(pat) => pat,
_ => P(pat),
})
}
_ => pat,
}
}
pub(super) fn could_ascription_be_path(&self, node: &ast::ExprKind) -> bool {
(self.token == token::Lt &&
self.look_ahead(1, |t| t.is_ident() && !t.is_reserved_ident()))
|| self.token.is_ident() &&
match node {
ast::ExprKind::Path(..) | ast::ExprKind::Field(..) => true,
_ => false,
} &&
!self.token.is_reserved_ident() &&
self.look_ahead(1, |t| t == &token::OpenDelim(token::Paren))
|| self.look_ahead(1, |t| t == &token::OpenDelim(token::Brace))
|| self.look_ahead(1, |t| t == &token::Colon) &&
self.look_ahead(2, |t| t == &token::Lt) &&
self.look_ahead(3, |t| t.is_ident())
|| self.look_ahead(1, |t| t == &token::Colon) &&
self.look_ahead(2, |t| t.is_ident())
|| self.look_ahead(1, |t| t == &token::ModSep)
&& (self.look_ahead(2, |t| t.is_ident()) ||
self.look_ahead(2, |t| t == &token::Lt))
}
pub(super) 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();
self.consume_block(delim, ConsumeClosingDelim::Yes);
self.mk_expr(lo.to(self.prev_token.span), ExprKind::Err, AttrVec::new())
}
}
}
pub(super) fn recover_closing_delimiter(
&mut self,
tokens: &[TokenKind],
mut err: DiagnosticBuilder<'a>,
) -> PResult<'a, bool> {
let mut pos = None;
for (i, unmatched) in self.unclosed_delims.iter().enumerate().rev() {
if tokens.contains(&token::CloseDelim(unmatched.expected_delim))
&& Some(self.token.span) > unmatched.unclosed_span
{
pos = Some(i);
}
}
match pos {
Some(pos) => {
let unmatched = self.unclosed_delims.remove(pos);
let delim = TokenType::Token(token::CloseDelim(unmatched.expected_delim));
if unmatched.found_delim.is_none() {
*self.sess.reached_eof.borrow_mut() = true;
}
if let Some(sp) = unmatched.unclosed_span {
err.span_label(sp, "unclosed delimiter");
}
let mut delim = delim.to_string();
delim.retain(|c| c != '`');
err.span_suggestion_short(
self.prev_token.span.shrink_to_hi(),
&format!("`{}` may belong here", delim),
delim,
Applicability::MaybeIncorrect,
);
if unmatched.found_delim.is_none() {
Err(err)
} else {
err.emit();
self.expected_tokens.clear();
Ok(true)
}
}
_ => Err(err),
}
}
pub(super) fn recover_stmt(&mut self) {
self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore)
}
pub(super) 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.kind {
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;
}
_ => self.bump(),
}
}
}
pub(super) fn check_for_for_in_in_typo(&mut self, in_span: Span) {
if self.eat_keyword(kw::In) {
self.struct_span_err(self.prev_token.span, "expected iterable, found keyword `in`")
.span_suggestion_short(
in_span.until(self.prev_token.span),
"remove the duplicated `in`",
String::new(),
Applicability::MachineApplicable,
)
.emit();
}
}
pub(super) fn expected_semi_or_open_brace<T>(&mut self) -> PResult<'a, T> {
let token_str = super::token_descr(&self.token);
let msg = &format!("expected `;` or `{{`, found {}", token_str);
let mut err = self.struct_span_err(self.token.span, msg);
err.span_label(self.token.span, "expected `;` or `{`");
Err(err)
}
pub(super) fn eat_incorrect_doc_comment_for_param_type(&mut self) {
if let token::DocComment(..) = self.token.kind {
self.struct_span_err(
self.token.span,
"documentation comments cannot be applied to a function parameter's type",
)
.span_label(self.token.span, "doc comments are not allowed here")
.emit();
self.bump();
} else if self.token == token::Pound
&& self.look_ahead(1, |t| *t == token::OpenDelim(token::Bracket))
{
let lo = self.token.span;
while self.token != token::CloseDelim(token::Bracket) {
self.bump();
}
let sp = lo.to(self.token.span);
self.bump();
self.struct_span_err(sp, "attributes cannot be applied to a function parameter's type")
.span_label(sp, "attributes are not allowed here")
.emit();
}
}
pub(super) fn parameter_without_type(
&mut self,
err: &mut DiagnosticBuilder<'_>,
pat: P<ast::Pat>,
require_name: bool,
first_param: bool,
) -> Option<Ident> {
if self.check_ident()
&& self.look_ahead(1, |t| *t == token::Comma || *t == token::CloseDelim(token::Paren))
{
let ident = self.parse_ident().unwrap();
let span = pat.span.with_hi(ident.span.hi());
err.span_suggestion(
span,
"declare the type after the parameter binding",
String::from("<identifier>: <type>"),
Applicability::HasPlaceholders,
);
return Some(ident);
} else if let PatKind::Ident(_, ident, _) = pat.kind {
if require_name
&& (self.token == token::Comma
|| self.token == token::Lt
|| self.token == token::CloseDelim(token::Paren))
{
if first_param {
err.span_suggestion(
pat.span,
"if this is a `self` type, give it a parameter name",
format!("self: {}", ident),
Applicability::MaybeIncorrect,
);
}
if self.token != token::Lt {
err.span_suggestion(
pat.span,
"if this is a parameter name, give it a type",
format!("{}: TypeName", ident),
Applicability::HasPlaceholders,
);
}
err.span_suggestion(
pat.span,
"if this is a type, explicitly ignore the parameter name",
format!("_: {}", ident),
Applicability::MachineApplicable,
);
err.note("anonymous parameters are removed in the 2018 edition (see RFC 1685)");
return if self.token == token::Lt { None } else { Some(ident) };
}
}
None
}
pub(super) fn recover_arg_parse(&mut self) -> PResult<'a, (P<ast::Pat>, P<ast::Ty>)> {
let pat = self.parse_pat(Some("argument name"))?;
self.expect(&token::Colon)?;
let ty = self.parse_ty()?;
struct_span_err!(
self.diagnostic(),
pat.span,
E0642,
"patterns aren't allowed in methods without bodies",
)
.span_suggestion_short(
pat.span,
"give this argument a name or use an underscore to ignore it",
"_".to_owned(),
Applicability::MachineApplicable,
)
.emit();
let pat =
P(Pat { kind: PatKind::Wild, span: pat.span, id: ast::DUMMY_NODE_ID, tokens: None });
Ok((pat, ty))
}
pub(super) fn recover_bad_self_param(&mut self, mut param: Param) -> PResult<'a, Param> {
let sp = param.pat.span;
param.ty.kind = TyKind::Err;
self.struct_span_err(sp, "unexpected `self` parameter in function")
.span_label(sp, "must be the first parameter of an associated function")
.emit();
Ok(param)
}
pub(super) fn consume_block(
&mut self,
delim: token::DelimToken,
consume_close: ConsumeClosingDelim,
) {
let mut brace_depth = 0;
loop {
if self.eat(&token::OpenDelim(delim)) {
brace_depth += 1;
} else if self.check(&token::CloseDelim(delim)) {
if brace_depth == 0 {
if let ConsumeClosingDelim::Yes = consume_close {
self.bump();
}
return;
} else {
self.bump();
brace_depth -= 1;
continue;
}
} else if self.token == token::Eof || self.eat(&token::CloseDelim(token::NoDelim)) {
return;
} else {
self.bump();
}
}
}
pub(super) fn expected_expression_found(&self) -> DiagnosticBuilder<'a> {
let (span, msg) = match (&self.token.kind, self.subparser_name) {
(&token::Eof, Some(origin)) => {
let sp = self.sess.source_map().next_point(self.token.span);
(sp, format!("expected expression, found end of {}", origin))
}
_ => (
self.token.span,
format!("expected expression, found {}", super::token_descr(&self.token),),
),
};
let mut err = self.struct_span_err(span, &msg);
let sp = self.sess.source_map().start_point(self.token.span);
if let Some(sp) = self.sess.ambiguous_block_expr_parse.borrow().get(&sp) {
self.sess.expr_parentheses_needed(&mut err, *sp, None);
}
err.span_label(span, "expected expression");
err
}
fn consume_tts(
&mut self,
mut acc: i64,
modifier: &[(token::TokenKind, i64)],
) {
while acc > 0 {
if let Some((_, val)) = modifier.iter().find(|(t, _)| *t == self.token.kind) {
acc += *val;
}
if self.token.kind == token::Eof {
break;
}
self.bump();
}
}
pub(super) fn deduplicate_recovered_params_names(&self, fn_inputs: &mut Vec<Param>) {
let mut seen_inputs = FxHashSet::default();
for input in fn_inputs.iter_mut() {
let opt_ident = if let (PatKind::Ident(_, ident, _), TyKind::Err) =
(&input.pat.kind, &input.ty.kind)
{
Some(*ident)
} else {
None
};
if let Some(ident) = opt_ident {
if seen_inputs.contains(&ident) {
input.pat.kind = PatKind::Wild;
}
seen_inputs.insert(ident);
}
}
}
}