Move some methods to diagnostics.rs
away from parser.rs
Move a bunch of error recovery methods to `diagnostics.rs` away from `parser.rs`.
This commit is contained in:
parent
976541884f
commit
d1364d5284
2 changed files with 462 additions and 413 deletions
|
@ -1,19 +1,104 @@
|
||||||
use crate::ast;
|
use crate::ast;
|
||||||
use crate::ast::{
|
use crate::ast::{
|
||||||
BlockCheckMode, Expr, ExprKind, Item, ItemKind, Pat, PatKind, QSelf, Ty, TyKind, VariantData,
|
BlockCheckMode, BinOpKind, Expr, ExprKind, Item, ItemKind, Pat, PatKind, PathSegment, QSelf,
|
||||||
|
Ty, TyKind, VariantData,
|
||||||
};
|
};
|
||||||
use crate::parse::parser::{BlockMode, PathStyle, SemiColonMode, TokenType};
|
use crate::parse::{SeqSep, token, PResult, Parser};
|
||||||
|
use crate::parse::parser::{BlockMode, PathStyle, SemiColonMode, TokenType, TokenExpectType};
|
||||||
use crate::parse::token;
|
use crate::parse::token;
|
||||||
use crate::parse::PResult;
|
|
||||||
use crate::parse::Parser;
|
|
||||||
use crate::print::pprust;
|
use crate::print::pprust;
|
||||||
use crate::ptr::P;
|
use crate::ptr::P;
|
||||||
use crate::source_map::Spanned;
|
use crate::source_map::Spanned;
|
||||||
use crate::symbol::kw;
|
use crate::symbol::kw;
|
||||||
use crate::ThinVec;
|
use crate::ThinVec;
|
||||||
use errors::{Applicability, DiagnosticBuilder};
|
use crate::tokenstream::TokenTree;
|
||||||
use log::debug;
|
use crate::util::parser::AssocOp;
|
||||||
use syntax_pos::{Span, DUMMY_SP};
|
use errors::{Applicability, DiagnosticBuilder, DiagnosticId, FatalError};
|
||||||
|
use syntax_pos::{Span, DUMMY_SP, MultiSpan};
|
||||||
|
use log::{debug, trace};
|
||||||
|
use std::slice;
|
||||||
|
|
||||||
|
pub enum Error {
|
||||||
|
FileNotFoundForModule {
|
||||||
|
mod_name: String,
|
||||||
|
default_path: String,
|
||||||
|
secondary_path: String,
|
||||||
|
dir_path: String,
|
||||||
|
},
|
||||||
|
DuplicatePaths {
|
||||||
|
mod_name: String,
|
||||||
|
default_path: String,
|
||||||
|
secondary_path: String,
|
||||||
|
},
|
||||||
|
UselessDocComment,
|
||||||
|
InclusiveRangeWithNoEnd,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error {
|
||||||
|
fn span_err<S: Into<MultiSpan>>(
|
||||||
|
self,
|
||||||
|
sp: S,
|
||||||
|
handler: &errors::Handler,
|
||||||
|
) -> DiagnosticBuilder<'_> {
|
||||||
|
match self {
|
||||||
|
Error::FileNotFoundForModule {
|
||||||
|
ref mod_name,
|
||||||
|
ref default_path,
|
||||||
|
ref secondary_path,
|
||||||
|
ref dir_path,
|
||||||
|
} => {
|
||||||
|
let mut err = struct_span_err!(
|
||||||
|
handler,
|
||||||
|
sp,
|
||||||
|
E0583,
|
||||||
|
"file not found for module `{}`",
|
||||||
|
mod_name,
|
||||||
|
);
|
||||||
|
err.help(&format!(
|
||||||
|
"name the file either {} or {} inside the directory \"{}\"",
|
||||||
|
default_path,
|
||||||
|
secondary_path,
|
||||||
|
dir_path,
|
||||||
|
));
|
||||||
|
err
|
||||||
|
}
|
||||||
|
Error::DuplicatePaths { ref mod_name, ref default_path, ref secondary_path } => {
|
||||||
|
let mut err = struct_span_err!(
|
||||||
|
handler,
|
||||||
|
sp,
|
||||||
|
E0584,
|
||||||
|
"file for module `{}` found at both {} and {}",
|
||||||
|
mod_name,
|
||||||
|
default_path,
|
||||||
|
secondary_path,
|
||||||
|
);
|
||||||
|
err.help("delete or rename one of them to remove the ambiguity");
|
||||||
|
err
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
Error::InclusiveRangeWithNoEnd => {
|
||||||
|
let mut err = struct_span_err!(
|
||||||
|
handler,
|
||||||
|
sp,
|
||||||
|
E0586,
|
||||||
|
"inclusive range with no end",
|
||||||
|
);
|
||||||
|
err.help("inclusive ranges must be bounded at the end (`..=b` or `a..=b`)");
|
||||||
|
err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait RecoverQPath: Sized + 'static {
|
pub trait RecoverQPath: Sized + 'static {
|
||||||
const PATH_STYLE: PathStyle = PathStyle::Expr;
|
const PATH_STYLE: PathStyle = PathStyle::Expr;
|
||||||
|
@ -63,6 +148,253 @@ impl RecoverQPath for Expr {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Parser<'a> {
|
impl<'a> Parser<'a> {
|
||||||
|
pub fn look_ahead<R, F>(&self, dist: usize, f: F) -> R where
|
||||||
|
F: FnOnce(&token::Token) -> R,
|
||||||
|
{
|
||||||
|
if dist == 0 {
|
||||||
|
return f(&self.token)
|
||||||
|
}
|
||||||
|
|
||||||
|
f(&match self.token_cursor.frame.tree_cursor.look_ahead(dist - 1) {
|
||||||
|
Some(tree) => match tree {
|
||||||
|
TokenTree::Token(_, tok) => tok,
|
||||||
|
TokenTree::Delimited(_, delim, _) => token::OpenDelim(delim),
|
||||||
|
},
|
||||||
|
None => token::CloseDelim(self.token_cursor.frame.delim),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
crate fn look_ahead_span(&self, dist: usize) -> Span {
|
||||||
|
if dist == 0 {
|
||||||
|
return self.span
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.token_cursor.frame.tree_cursor.look_ahead(dist - 1) {
|
||||||
|
Some(TokenTree::Token(span, _)) => span,
|
||||||
|
Some(TokenTree::Delimited(span, ..)) => span.entire(),
|
||||||
|
None => self.look_ahead_span(dist - 1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fatal(&self, m: &str) -> DiagnosticBuilder<'a> {
|
||||||
|
self.sess.span_diagnostic.struct_span_fatal(self.span, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, m: &str) -> DiagnosticBuilder<'a> {
|
||||||
|
self.sess.span_diagnostic.struct_span_fatal(sp, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn span_fatal_err<S: Into<MultiSpan>>(&self, sp: S, err: Error) -> DiagnosticBuilder<'a> {
|
||||||
|
err.span_err(sp, self.diagnostic())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bug(&self, m: &str) -> ! {
|
||||||
|
self.sess.span_diagnostic.span_bug(self.span, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn span_err<S: Into<MultiSpan>>(&self, sp: S, m: &str) {
|
||||||
|
self.sess.span_diagnostic.span_err(sp, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
crate fn struct_span_err<S: Into<MultiSpan>>(&self, sp: S, m: &str) -> DiagnosticBuilder<'a> {
|
||||||
|
self.sess.span_diagnostic.struct_span_err(sp, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
crate fn span_bug<S: Into<MultiSpan>>(&self, sp: S, m: &str) -> ! {
|
||||||
|
self.sess.span_diagnostic.span_bug(sp, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
crate fn cancel(&self, err: &mut DiagnosticBuilder<'_>) {
|
||||||
|
self.sess.span_diagnostic.cancel(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
crate fn diagnostic(&self) -> &'a errors::Handler {
|
||||||
|
&self.sess.span_diagnostic
|
||||||
|
}
|
||||||
|
|
||||||
|
crate fn expected_ident_found(&self) -> DiagnosticBuilder<'a> {
|
||||||
|
let mut err = self.struct_span_err(
|
||||||
|
self.span,
|
||||||
|
&format!("expected identifier, found {}", self.this_token_descr()),
|
||||||
|
);
|
||||||
|
if let token::Ident(ident, false) = &self.token {
|
||||||
|
if ident.is_raw_guess() {
|
||||||
|
err.span_suggestion(
|
||||||
|
self.span,
|
||||||
|
"you can escape reserved keywords to use them as identifiers",
|
||||||
|
format!("r#{}", ident),
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(token_descr) = self.token_descr() {
|
||||||
|
err.span_label(self.span, format!("expected identifier, found {}", token_descr));
|
||||||
|
} else {
|
||||||
|
err.span_label(self.span, "expected identifier");
|
||||||
|
if self.token == token::Comma && self.look_ahead(1, |t| t.is_ident()) {
|
||||||
|
err.span_suggestion(
|
||||||
|
self.span,
|
||||||
|
"remove this comma",
|
||||||
|
String::new(),
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Eats and discards tokens until one of `kets` is encountered. Respects token trees,
|
||||||
|
/// passes through any errors encountered. Used for error recovery.
|
||||||
|
crate fn eat_to_tokens(&mut self, kets: &[&token::Token]) {
|
||||||
|
let handler = self.diagnostic();
|
||||||
|
|
||||||
|
if let Err(ref mut err) = self.parse_seq_to_before_tokens(
|
||||||
|
kets,
|
||||||
|
SeqSep::none(),
|
||||||
|
TokenExpectType::Expect,
|
||||||
|
|p| Ok(p.parse_token_tree()),
|
||||||
|
) {
|
||||||
|
handler.cancel(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function checks if there are trailing angle brackets and produces
|
||||||
|
/// a diagnostic to suggest removing them.
|
||||||
|
///
|
||||||
|
/// ```ignore (diagnostic)
|
||||||
|
/// let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>>>();
|
||||||
|
/// ^^ help: remove extra angle brackets
|
||||||
|
/// ```
|
||||||
|
crate fn check_trailing_angle_brackets(&mut self, segment: &PathSegment, end: token::Token) {
|
||||||
|
// This function is intended to be invoked after parsing a path segment where there are two
|
||||||
|
// cases:
|
||||||
|
//
|
||||||
|
// 1. A specific token is expected after the path segment.
|
||||||
|
// eg. `x.foo(`, `x.foo::<u32>(` (parenthesis - method call),
|
||||||
|
// `Foo::`, or `Foo::<Bar>::` (mod sep - continued path).
|
||||||
|
// 2. No specific token is expected after the path segment.
|
||||||
|
// eg. `x.foo` (field access)
|
||||||
|
//
|
||||||
|
// This function is called after parsing `.foo` and before parsing the token `end` (if
|
||||||
|
// present). This includes any angle bracket arguments, such as `.foo::<u32>` or
|
||||||
|
// `Foo::<Bar>`.
|
||||||
|
|
||||||
|
// We only care about trailing angle brackets if we previously parsed angle bracket
|
||||||
|
// arguments. This helps stop us incorrectly suggesting that extra angle brackets be
|
||||||
|
// removed in this case:
|
||||||
|
//
|
||||||
|
// `x.foo >> (3)` (where `x.foo` is a `u32` for example)
|
||||||
|
//
|
||||||
|
// This case is particularly tricky as we won't notice it just looking at the tokens -
|
||||||
|
// it will appear the same (in terms of upcoming tokens) as below (since the `::<u32>` will
|
||||||
|
// have already been parsed):
|
||||||
|
//
|
||||||
|
// `x.foo::<u32>>>(3)`
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep the span at the start so we can highlight the sequence of `>` characters to be
|
||||||
|
// removed.
|
||||||
|
let lo = self.span;
|
||||||
|
|
||||||
|
// We need to look-ahead to see if we have `>` characters without moving the cursor forward
|
||||||
|
// (since we might have the field access case and the characters we're eating are
|
||||||
|
// actual operators and not trailing characters - ie `x.foo >> 3`).
|
||||||
|
let mut position = 0;
|
||||||
|
|
||||||
|
// We can encounter `>` or `>>` tokens in any order, so we need to keep track of how
|
||||||
|
// many of each (so we can correctly pluralize our error messages) and continue to
|
||||||
|
// advance.
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we didn't find any trailing `>` characters, then we have nothing to error about.
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, double check that we have our end token as otherwise this is the
|
||||||
|
// second case.
|
||||||
|
if self.look_ahead(position, |t| {
|
||||||
|
trace!("check_trailing_angle_brackets: t={:?}", t);
|
||||||
|
*t == end
|
||||||
|
}) {
|
||||||
|
// Eat from where we started until the end token so that parsing can continue
|
||||||
|
// as if we didn't have those extra angle brackets.
|
||||||
|
self.eat_to_tokens(&[&end]);
|
||||||
|
let span = lo.until(self.span);
|
||||||
|
|
||||||
|
let plural = number_of_gt > 1 || number_of_shr >= 1;
|
||||||
|
self.diagnostic()
|
||||||
|
.struct_span_err(
|
||||||
|
span,
|
||||||
|
&format!("unmatched angle bracket{}", if plural { "s" } else { "" }),
|
||||||
|
)
|
||||||
|
.span_suggestion(
|
||||||
|
span,
|
||||||
|
&format!("remove extra angle bracket{}", if plural { "s" } else { "" }),
|
||||||
|
String::new(),
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
)
|
||||||
|
.emit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Produce an error if comparison operators are chained (RFC #558).
|
||||||
|
/// We only need to check lhs, not rhs, because all comparison ops
|
||||||
|
/// have same precedence and are left-associative
|
||||||
|
crate fn check_no_chained_comparison(&self, lhs: &Expr, outer_op: &AssocOp) {
|
||||||
|
debug_assert!(outer_op.is_comparison(),
|
||||||
|
"check_no_chained_comparison: {:?} is not comparison",
|
||||||
|
outer_op);
|
||||||
|
match lhs.node {
|
||||||
|
ExprKind::Binary(op, _, _) if op.node.is_comparison() => {
|
||||||
|
// respan to include both operators
|
||||||
|
let op_span = op.span.to(self.span);
|
||||||
|
let mut err = self.diagnostic().struct_span_err(op_span,
|
||||||
|
"chained comparison operators require parentheses");
|
||||||
|
if op.node == BinOpKind::Lt &&
|
||||||
|
*outer_op == AssocOp::Less || // Include `<` to provide this recommendation
|
||||||
|
*outer_op == AssocOp::Greater // even in a case like the following:
|
||||||
|
{ // Foo<Bar<Baz<Qux, ()>>>
|
||||||
|
err.help(
|
||||||
|
"use `::<...>` instead of `<...>` if you meant to specify type arguments");
|
||||||
|
err.help("or use `(...)` if you meant to specify fn arguments");
|
||||||
|
}
|
||||||
|
err.emit();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
crate fn maybe_report_ambiguous_plus(
|
crate fn maybe_report_ambiguous_plus(
|
||||||
&mut self,
|
&mut self,
|
||||||
allow_plus: bool,
|
allow_plus: bool,
|
||||||
|
@ -594,6 +926,96 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
crate fn check_for_for_in_in_typo(&mut self, in_span: Span) {
|
||||||
|
if self.eat_keyword(kw::In) {
|
||||||
|
// a common typo: `for _ in in bar {}`
|
||||||
|
let mut err = self.sess.span_diagnostic.struct_span_err(
|
||||||
|
self.prev_span,
|
||||||
|
"expected iterable, found keyword `in`",
|
||||||
|
);
|
||||||
|
err.span_suggestion_short(
|
||||||
|
in_span.until(self.prev_span),
|
||||||
|
"remove the duplicated `in`",
|
||||||
|
String::new(),
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
err.note("if you meant to use emplacement syntax, it is obsolete (for now, anyway)");
|
||||||
|
err.note("for more information on the status of emplacement syntax, see <\
|
||||||
|
https://github.com/rust-lang/rust/issues/27779#issuecomment-378416911>");
|
||||||
|
err.emit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
crate fn expected_semi_or_open_brace(&mut self) -> PResult<'a, ast::TraitItem> {
|
||||||
|
let token_str = self.this_token_descr();
|
||||||
|
let mut err = self.fatal(&format!("expected `;` or `{{`, found {}", token_str));
|
||||||
|
err.span_label(self.span, "expected `;` or `{`");
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
crate fn eat_incorrect_doc_comment(&mut self, applied_to: &str) {
|
||||||
|
if let token::DocComment(_) = self.token {
|
||||||
|
let mut err = self.diagnostic().struct_span_err(
|
||||||
|
self.span,
|
||||||
|
&format!("documentation comments cannot be applied to {}", applied_to),
|
||||||
|
);
|
||||||
|
err.span_label(self.span, "doc comments are not allowed here");
|
||||||
|
err.emit();
|
||||||
|
self.bump();
|
||||||
|
} else if self.token == token::Pound && self.look_ahead(1, |t| {
|
||||||
|
*t == token::OpenDelim(token::Bracket)
|
||||||
|
}) {
|
||||||
|
let lo = self.span;
|
||||||
|
// Skip every token until next possible arg.
|
||||||
|
while self.token != token::CloseDelim(token::Bracket) {
|
||||||
|
self.bump();
|
||||||
|
}
|
||||||
|
let sp = lo.to(self.span);
|
||||||
|
self.bump();
|
||||||
|
let mut err = self.diagnostic().struct_span_err(
|
||||||
|
sp,
|
||||||
|
&format!("attributes cannot be applied to {}", applied_to),
|
||||||
|
);
|
||||||
|
err.span_label(sp, "attributes are not allowed here");
|
||||||
|
err.emit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
crate fn argument_without_type(
|
||||||
|
&mut self,
|
||||||
|
err: &mut DiagnosticBuilder<'_>,
|
||||||
|
pat: P<ast::Pat>,
|
||||||
|
require_name: bool,
|
||||||
|
is_trait_item: bool,
|
||||||
|
) {
|
||||||
|
// If we find a pattern followed by an identifier, it could be an (incorrect)
|
||||||
|
// C-style parameter declaration.
|
||||||
|
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,
|
||||||
|
);
|
||||||
|
} else if require_name && is_trait_item {
|
||||||
|
if let PatKind::Ident(_, ident, _) = pat.node {
|
||||||
|
err.span_suggestion(
|
||||||
|
pat.span,
|
||||||
|
"explicitly ignore parameter",
|
||||||
|
format!("_: {}", ident),
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
err.note("anonymous parameters are removed in the 2018 edition (see RFC 1685)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
crate fn recover_arg_parse(&mut self) -> PResult<'a, (P<ast::Pat>, P<ast::Ty>)> {
|
crate fn recover_arg_parse(&mut self) -> PResult<'a, (P<ast::Pat>, P<ast::Ty>)> {
|
||||||
let pat = self.parse_pat(Some("argument name"))?;
|
let pat = self.parse_pat(Some("argument name"))?;
|
||||||
self.expect(&token::Colon)?;
|
self.expect(&token::Colon)?;
|
||||||
|
|
|
@ -47,20 +47,17 @@ use crate::parse::PResult;
|
||||||
use crate::ThinVec;
|
use crate::ThinVec;
|
||||||
use crate::tokenstream::{self, DelimSpan, TokenTree, TokenStream, TreeAndJoint};
|
use crate::tokenstream::{self, DelimSpan, TokenTree, TokenStream, TreeAndJoint};
|
||||||
use crate::symbol::{kw, sym, Symbol};
|
use crate::symbol::{kw, sym, Symbol};
|
||||||
|
use crate::parse::diagnostics::Error;
|
||||||
|
|
||||||
use errors::{Applicability, DiagnosticBuilder, DiagnosticId, FatalError};
|
use errors::{Applicability, DiagnosticBuilder, DiagnosticId};
|
||||||
use rustc_target::spec::abi::{self, Abi};
|
use rustc_target::spec::abi::{self, Abi};
|
||||||
use syntax_pos::{
|
use syntax_pos::{Span, BytePos, DUMMY_SP, FileName, hygiene::CompilerDesugaringKind};
|
||||||
BytePos, DUMMY_SP, FileName, MultiSpan, Span,
|
use log::debug;
|
||||||
hygiene::CompilerDesugaringKind,
|
|
||||||
};
|
|
||||||
use log::{debug, trace};
|
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::path::{self, Path, PathBuf};
|
use std::path::{self, Path, PathBuf};
|
||||||
use std::slice;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
/// Whether the type alias or associated type is a concrete type or an existential type
|
/// Whether the type alias or associated type is a concrete type or an existential type
|
||||||
|
@ -217,7 +214,7 @@ pub struct Parser<'a> {
|
||||||
/// into modules, and sub-parsers have new values for this name.
|
/// into modules, and sub-parsers have new values for this name.
|
||||||
pub root_module_name: Option<String>,
|
pub root_module_name: Option<String>,
|
||||||
crate expected_tokens: Vec<TokenType>,
|
crate expected_tokens: Vec<TokenType>,
|
||||||
token_cursor: TokenCursor,
|
crate token_cursor: TokenCursor,
|
||||||
desugar_doc_comments: bool,
|
desugar_doc_comments: bool,
|
||||||
/// Whether we should configure out of line modules as we parse.
|
/// Whether we should configure out of line modules as we parse.
|
||||||
pub cfg_mods: bool,
|
pub cfg_mods: bool,
|
||||||
|
@ -232,7 +229,7 @@ pub struct Parser<'a> {
|
||||||
/// it gets removed from here. Every entry left at the end gets emitted as an independent
|
/// it gets removed from here. Every entry left at the end gets emitted as an independent
|
||||||
/// error.
|
/// error.
|
||||||
crate unclosed_delims: Vec<UnmatchedBrace>,
|
crate unclosed_delims: Vec<UnmatchedBrace>,
|
||||||
last_unexpected_token_span: Option<Span>,
|
crate last_unexpected_token_span: Option<Span>,
|
||||||
/// If present, this `Parser` is not parsing Rust code but rather a macro call.
|
/// If present, this `Parser` is not parsing Rust code but rather a macro call.
|
||||||
crate subparser_name: Option<&'static str>,
|
crate subparser_name: Option<&'static str>,
|
||||||
}
|
}
|
||||||
|
@ -245,19 +242,19 @@ impl<'a> Drop for Parser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct TokenCursor {
|
crate struct TokenCursor {
|
||||||
frame: TokenCursorFrame,
|
crate frame: TokenCursorFrame,
|
||||||
stack: Vec<TokenCursorFrame>,
|
crate stack: Vec<TokenCursorFrame>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct TokenCursorFrame {
|
crate struct TokenCursorFrame {
|
||||||
delim: token::DelimToken,
|
crate delim: token::DelimToken,
|
||||||
span: DelimSpan,
|
crate span: DelimSpan,
|
||||||
open_delim: bool,
|
crate open_delim: bool,
|
||||||
tree_cursor: tokenstream::Cursor,
|
crate tree_cursor: tokenstream::Cursor,
|
||||||
close_delim: bool,
|
crate close_delim: bool,
|
||||||
last_token: LastToken,
|
crate last_token: LastToken,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is used in `TokenCursorFrame` above to track tokens that are consumed
|
/// This is used in `TokenCursorFrame` above to track tokens that are consumed
|
||||||
|
@ -278,7 +275,7 @@ struct TokenCursorFrame {
|
||||||
/// You can find some more example usage of this in the `collect_tokens` method
|
/// You can find some more example usage of this in the `collect_tokens` method
|
||||||
/// on the parser.
|
/// on the parser.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
enum LastToken {
|
crate enum LastToken {
|
||||||
Collecting(Vec<TreeAndJoint>),
|
Collecting(Vec<TreeAndJoint>),
|
||||||
Was(Option<TreeAndJoint>),
|
Was(Option<TreeAndJoint>),
|
||||||
}
|
}
|
||||||
|
@ -430,65 +427,6 @@ pub struct ModulePathSuccess {
|
||||||
warn: bool,
|
warn: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Error {
|
|
||||||
FileNotFoundForModule {
|
|
||||||
mod_name: String,
|
|
||||||
default_path: String,
|
|
||||||
secondary_path: String,
|
|
||||||
dir_path: String,
|
|
||||||
},
|
|
||||||
DuplicatePaths {
|
|
||||||
mod_name: String,
|
|
||||||
default_path: String,
|
|
||||||
secondary_path: String,
|
|
||||||
},
|
|
||||||
UselessDocComment,
|
|
||||||
InclusiveRangeWithNoEnd,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error {
|
|
||||||
fn span_err<S: Into<MultiSpan>>(self,
|
|
||||||
sp: S,
|
|
||||||
handler: &errors::Handler) -> DiagnosticBuilder<'_> {
|
|
||||||
match self {
|
|
||||||
Error::FileNotFoundForModule { ref mod_name,
|
|
||||||
ref default_path,
|
|
||||||
ref secondary_path,
|
|
||||||
ref dir_path } => {
|
|
||||||
let mut err = struct_span_err!(handler, sp, E0583,
|
|
||||||
"file not found for module `{}`", mod_name);
|
|
||||||
err.help(&format!("name the file either {} or {} inside the directory \"{}\"",
|
|
||||||
default_path,
|
|
||||||
secondary_path,
|
|
||||||
dir_path));
|
|
||||||
err
|
|
||||||
}
|
|
||||||
Error::DuplicatePaths { ref mod_name, ref default_path, ref secondary_path } => {
|
|
||||||
let mut err = struct_span_err!(handler, sp, E0584,
|
|
||||||
"file for module `{}` found at both {} and {}",
|
|
||||||
mod_name,
|
|
||||||
default_path,
|
|
||||||
secondary_path);
|
|
||||||
err.help("delete or rename one of them to remove the ambiguity");
|
|
||||||
err
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
Error::InclusiveRangeWithNoEnd => {
|
|
||||||
let mut err = struct_span_err!(handler, sp, E0586,
|
|
||||||
"inclusive range with no end");
|
|
||||||
err.help("inclusive ranges must be bounded at the end (`..=b` or `a..=b`)");
|
|
||||||
err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum LhsExpr {
|
enum LhsExpr {
|
||||||
NotYetParsed,
|
NotYetParsed,
|
||||||
|
@ -529,7 +467,7 @@ fn dummy_arg(span: Span) -> Arg {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
enum TokenExpectType {
|
crate enum TokenExpectType {
|
||||||
Expect,
|
Expect,
|
||||||
NoExpect,
|
NoExpect,
|
||||||
}
|
}
|
||||||
|
@ -610,7 +548,7 @@ impl<'a> Parser<'a> {
|
||||||
pprust::token_to_string(&self.token)
|
pprust::token_to_string(&self.token)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn token_descr(&self) -> Option<&'static str> {
|
crate fn token_descr(&self) -> Option<&'static str> {
|
||||||
Some(match &self.token {
|
Some(match &self.token {
|
||||||
t if t.is_special_ident() => "reserved identifier",
|
t if t.is_special_ident() => "reserved identifier",
|
||||||
t if t.is_used_keyword() => "keyword",
|
t if t.is_used_keyword() => "keyword",
|
||||||
|
@ -801,9 +739,10 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the span of expr, if it was not interpolated or the span of the interpolated token.
|
/// Returns the span of expr, if it was not interpolated or the span of the interpolated token.
|
||||||
fn interpolated_or_expr_span(&self,
|
fn interpolated_or_expr_span(
|
||||||
expr: PResult<'a, P<Expr>>)
|
&self,
|
||||||
-> PResult<'a, (Span, P<Expr>)> {
|
expr: PResult<'a, P<Expr>>,
|
||||||
|
) -> PResult<'a, (Span, P<Expr>)> {
|
||||||
expr.map(|e| {
|
expr.map(|e| {
|
||||||
if self.prev_token_kind == PrevTokenKind::Interpolated {
|
if self.prev_token_kind == PrevTokenKind::Interpolated {
|
||||||
(self.prev_span, e)
|
(self.prev_span, e)
|
||||||
|
@ -813,36 +752,6 @@ impl<'a> Parser<'a> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expected_ident_found(&self) -> DiagnosticBuilder<'a> {
|
|
||||||
let mut err = self.struct_span_err(self.span,
|
|
||||||
&format!("expected identifier, found {}",
|
|
||||||
self.this_token_descr()));
|
|
||||||
if let token::Ident(ident, false) = &self.token {
|
|
||||||
if ident.is_raw_guess() {
|
|
||||||
err.span_suggestion(
|
|
||||||
self.span,
|
|
||||||
"you can escape reserved keywords to use them as identifiers",
|
|
||||||
format!("r#{}", ident),
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(token_descr) = self.token_descr() {
|
|
||||||
err.span_label(self.span, format!("expected identifier, found {}", token_descr));
|
|
||||||
} else {
|
|
||||||
err.span_label(self.span, "expected identifier");
|
|
||||||
if self.token == token::Comma && self.look_ahead(1, |t| t.is_ident()) {
|
|
||||||
err.span_suggestion(
|
|
||||||
self.span,
|
|
||||||
"remove this comma",
|
|
||||||
String::new(),
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_ident(&mut self) -> PResult<'a, ast::Ident> {
|
pub fn parse_ident(&mut self) -> PResult<'a, ast::Ident> {
|
||||||
self.parse_ident_common(true)
|
self.parse_ident_common(true)
|
||||||
}
|
}
|
||||||
|
@ -925,7 +834,7 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_ident(&mut self) -> bool {
|
crate fn check_ident(&mut self) -> bool {
|
||||||
if self.token.is_ident() {
|
if self.token.is_ident() {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
|
@ -1115,19 +1024,6 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Eats and discards tokens until one of `kets` is encountered. Respects token trees,
|
|
||||||
/// passes through any errors encountered. Used for error recovery.
|
|
||||||
fn eat_to_tokens(&mut self, kets: &[&token::Token]) {
|
|
||||||
let handler = self.diagnostic();
|
|
||||||
|
|
||||||
if let Err(ref mut err) = self.parse_seq_to_before_tokens(kets,
|
|
||||||
SeqSep::none(),
|
|
||||||
TokenExpectType::Expect,
|
|
||||||
|p| Ok(p.parse_token_tree())) {
|
|
||||||
handler.cancel(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parses a sequence, including the closing delimiter. The function
|
/// Parses a sequence, including the closing delimiter. The function
|
||||||
/// `f` must consume tokens until reaching the next separator or
|
/// `f` must consume tokens until reaching the next separator or
|
||||||
/// closing bracket.
|
/// closing bracket.
|
||||||
|
@ -1159,7 +1055,7 @@ impl<'a> Parser<'a> {
|
||||||
self.parse_seq_to_before_tokens(&[ket], sep, TokenExpectType::Expect, f)
|
self.parse_seq_to_before_tokens(&[ket], sep, TokenExpectType::Expect, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_seq_to_before_tokens<T, F>(
|
crate fn parse_seq_to_before_tokens<T, F>(
|
||||||
&mut self,
|
&mut self,
|
||||||
kets: &[&token::Token],
|
kets: &[&token::Token],
|
||||||
sep: SeqSep,
|
sep: SeqSep,
|
||||||
|
@ -1292,63 +1188,6 @@ impl<'a> Parser<'a> {
|
||||||
self.expected_tokens.clear();
|
self.expected_tokens.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn look_ahead<R, F>(&self, dist: usize, f: F) -> R where
|
|
||||||
F: FnOnce(&token::Token) -> R,
|
|
||||||
{
|
|
||||||
if dist == 0 {
|
|
||||||
return f(&self.token)
|
|
||||||
}
|
|
||||||
|
|
||||||
f(&match self.token_cursor.frame.tree_cursor.look_ahead(dist - 1) {
|
|
||||||
Some(tree) => match tree {
|
|
||||||
TokenTree::Token(_, tok) => tok,
|
|
||||||
TokenTree::Delimited(_, delim, _) => token::OpenDelim(delim),
|
|
||||||
},
|
|
||||||
None => token::CloseDelim(self.token_cursor.frame.delim),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
crate fn look_ahead_span(&self, dist: usize) -> Span {
|
|
||||||
if dist == 0 {
|
|
||||||
return self.span
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.token_cursor.frame.tree_cursor.look_ahead(dist - 1) {
|
|
||||||
Some(TokenTree::Token(span, _)) => span,
|
|
||||||
Some(TokenTree::Delimited(span, ..)) => span.entire(),
|
|
||||||
None => self.look_ahead_span(dist - 1),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn fatal(&self, m: &str) -> DiagnosticBuilder<'a> {
|
|
||||||
self.sess.span_diagnostic.struct_span_fatal(self.span, m)
|
|
||||||
}
|
|
||||||
pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, m: &str) -> DiagnosticBuilder<'a> {
|
|
||||||
self.sess.span_diagnostic.struct_span_fatal(sp, m)
|
|
||||||
}
|
|
||||||
fn span_fatal_err<S: Into<MultiSpan>>(&self, sp: S, err: Error) -> DiagnosticBuilder<'a> {
|
|
||||||
err.span_err(sp, self.diagnostic())
|
|
||||||
}
|
|
||||||
fn bug(&self, m: &str) -> ! {
|
|
||||||
self.sess.span_diagnostic.span_bug(self.span, m)
|
|
||||||
}
|
|
||||||
fn span_err<S: Into<MultiSpan>>(&self, sp: S, m: &str) {
|
|
||||||
self.sess.span_diagnostic.span_err(sp, m)
|
|
||||||
}
|
|
||||||
crate fn struct_span_err<S: Into<MultiSpan>>(&self, sp: S, m: &str) -> DiagnosticBuilder<'a> {
|
|
||||||
self.sess.span_diagnostic.struct_span_err(sp, m)
|
|
||||||
}
|
|
||||||
crate fn span_bug<S: Into<MultiSpan>>(&self, sp: S, m: &str) -> ! {
|
|
||||||
self.sess.span_diagnostic.span_bug(sp, m)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cancel(&self, err: &mut DiagnosticBuilder<'_>) {
|
|
||||||
self.sess.span_diagnostic.cancel(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
crate fn diagnostic(&self) -> &'a errors::Handler {
|
|
||||||
&self.sess.span_diagnostic
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Is the current token one of the keywords that signals a bare function type?
|
/// Is the current token one of the keywords that signals a bare function type?
|
||||||
fn token_is_bare_fn_keyword(&mut self) -> bool {
|
fn token_is_bare_fn_keyword(&mut self) -> bool {
|
||||||
self.check_keyword(kw::Fn) ||
|
self.check_keyword(kw::Fn) ||
|
||||||
|
@ -1507,20 +1346,12 @@ impl<'a> Parser<'a> {
|
||||||
Some(body)
|
Some(body)
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let token_str = self.this_token_descr();
|
return self.expected_semi_or_open_brace();
|
||||||
let mut err = self.fatal(&format!("expected `;` or `{{`, found {}",
|
|
||||||
token_str));
|
|
||||||
err.span_label(self.span, "expected `;` or `{`");
|
|
||||||
return Err(err);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let token_str = self.this_token_descr();
|
return self.expected_semi_or_open_brace();
|
||||||
let mut err = self.fatal(&format!("expected `;` or `{{`, found {}",
|
|
||||||
token_str));
|
|
||||||
err.span_label(self.span, "expected `;` or `{`");
|
|
||||||
return Err(err);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
(ident, ast::TraitItemKind::Method(sig, body), generics)
|
(ident, ast::TraitItemKind::Method(sig, body), generics)
|
||||||
|
@ -1776,34 +1607,6 @@ impl<'a> Parser<'a> {
|
||||||
|
|
||||||
/// Skips unexpected attributes and doc comments in this position and emits an appropriate
|
/// Skips unexpected attributes and doc comments in this position and emits an appropriate
|
||||||
/// error.
|
/// error.
|
||||||
fn eat_incorrect_doc_comment(&mut self, applied_to: &str) {
|
|
||||||
if let token::DocComment(_) = self.token {
|
|
||||||
let mut err = self.diagnostic().struct_span_err(
|
|
||||||
self.span,
|
|
||||||
&format!("documentation comments cannot be applied to {}", applied_to),
|
|
||||||
);
|
|
||||||
err.span_label(self.span, "doc comments are not allowed here");
|
|
||||||
err.emit();
|
|
||||||
self.bump();
|
|
||||||
} else if self.token == token::Pound && self.look_ahead(1, |t| {
|
|
||||||
*t == token::OpenDelim(token::Bracket)
|
|
||||||
}) {
|
|
||||||
let lo = self.span;
|
|
||||||
// Skip every token until next possible arg.
|
|
||||||
while self.token != token::CloseDelim(token::Bracket) {
|
|
||||||
self.bump();
|
|
||||||
}
|
|
||||||
let sp = lo.to(self.span);
|
|
||||||
self.bump();
|
|
||||||
let mut err = self.diagnostic().struct_span_err(
|
|
||||||
sp,
|
|
||||||
&format!("attributes cannot be applied to {}", applied_to),
|
|
||||||
);
|
|
||||||
err.span_label(sp, "attributes are not allowed here");
|
|
||||||
err.emit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This version of parse arg doesn't necessarily require identifier names.
|
/// This version of parse arg doesn't necessarily require identifier names.
|
||||||
fn parse_arg_general(
|
fn parse_arg_general(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -1858,30 +1661,7 @@ impl<'a> Parser<'a> {
|
||||||
// Recover from attempting to parse the argument as a type without pattern.
|
// Recover from attempting to parse the argument as a type without pattern.
|
||||||
err.cancel();
|
err.cancel();
|
||||||
mem::replace(self, parser_snapshot_before_ty);
|
mem::replace(self, parser_snapshot_before_ty);
|
||||||
let pat = self.parse_pat(Some("argument name"))?;
|
self.recover_arg_parse()?
|
||||||
self.expect(&token::Colon)?;
|
|
||||||
let ty = self.parse_ty()?;
|
|
||||||
|
|
||||||
let mut err = self.diagnostic().struct_span_err_with_code(
|
|
||||||
pat.span,
|
|
||||||
"patterns aren't allowed in methods without bodies",
|
|
||||||
DiagnosticId::Error("E0642".into()),
|
|
||||||
);
|
|
||||||
err.span_suggestion_short(
|
|
||||||
pat.span,
|
|
||||||
"give this argument a name or use an underscore to ignore it",
|
|
||||||
"_".to_owned(),
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
err.emit();
|
|
||||||
|
|
||||||
// Pretend the pattern is `_`, to avoid duplicate errors from AST validation.
|
|
||||||
let pat = P(Pat {
|
|
||||||
node: PatKind::Wild,
|
|
||||||
span: pat.span,
|
|
||||||
id: ast::DUMMY_NODE_ID
|
|
||||||
});
|
|
||||||
(pat, ty)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1889,11 +1669,6 @@ impl<'a> Parser<'a> {
|
||||||
Ok(Arg { ty, pat, id: ast::DUMMY_NODE_ID, source: ast::ArgSource::Normal })
|
Ok(Arg { ty, pat, id: ast::DUMMY_NODE_ID, source: ast::ArgSource::Normal })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses a single function argument.
|
|
||||||
crate fn parse_arg(&mut self) -> PResult<'a, Arg> {
|
|
||||||
self.parse_arg_general(true, false, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parses an argument in a lambda header (e.g., `|arg, arg|`).
|
/// Parses an argument in a lambda header (e.g., `|arg, arg|`).
|
||||||
fn parse_fn_block_arg(&mut self) -> PResult<'a, Arg> {
|
fn parse_fn_block_arg(&mut self) -> PResult<'a, Arg> {
|
||||||
let pat = self.parse_pat(Some("argument name"))?;
|
let pat = self.parse_pat(Some("argument name"))?;
|
||||||
|
@ -2858,116 +2633,6 @@ impl<'a> Parser<'a> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function checks if there are trailing angle brackets and produces
|
|
||||||
/// a diagnostic to suggest removing them.
|
|
||||||
///
|
|
||||||
/// ```ignore (diagnostic)
|
|
||||||
/// let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>>>();
|
|
||||||
/// ^^ help: remove extra angle brackets
|
|
||||||
/// ```
|
|
||||||
fn check_trailing_angle_brackets(&mut self, segment: &PathSegment, end: token::Token) {
|
|
||||||
// This function is intended to be invoked after parsing a path segment where there are two
|
|
||||||
// cases:
|
|
||||||
//
|
|
||||||
// 1. A specific token is expected after the path segment.
|
|
||||||
// eg. `x.foo(`, `x.foo::<u32>(` (parenthesis - method call),
|
|
||||||
// `Foo::`, or `Foo::<Bar>::` (mod sep - continued path).
|
|
||||||
// 2. No specific token is expected after the path segment.
|
|
||||||
// eg. `x.foo` (field access)
|
|
||||||
//
|
|
||||||
// This function is called after parsing `.foo` and before parsing the token `end` (if
|
|
||||||
// present). This includes any angle bracket arguments, such as `.foo::<u32>` or
|
|
||||||
// `Foo::<Bar>`.
|
|
||||||
|
|
||||||
// We only care about trailing angle brackets if we previously parsed angle bracket
|
|
||||||
// arguments. This helps stop us incorrectly suggesting that extra angle brackets be
|
|
||||||
// removed in this case:
|
|
||||||
//
|
|
||||||
// `x.foo >> (3)` (where `x.foo` is a `u32` for example)
|
|
||||||
//
|
|
||||||
// This case is particularly tricky as we won't notice it just looking at the tokens -
|
|
||||||
// it will appear the same (in terms of upcoming tokens) as below (since the `::<u32>` will
|
|
||||||
// have already been parsed):
|
|
||||||
//
|
|
||||||
// `x.foo::<u32>>>(3)`
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep the span at the start so we can highlight the sequence of `>` characters to be
|
|
||||||
// removed.
|
|
||||||
let lo = self.span;
|
|
||||||
|
|
||||||
// We need to look-ahead to see if we have `>` characters without moving the cursor forward
|
|
||||||
// (since we might have the field access case and the characters we're eating are
|
|
||||||
// actual operators and not trailing characters - ie `x.foo >> 3`).
|
|
||||||
let mut position = 0;
|
|
||||||
|
|
||||||
// We can encounter `>` or `>>` tokens in any order, so we need to keep track of how
|
|
||||||
// many of each (so we can correctly pluralize our error messages) and continue to
|
|
||||||
// advance.
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we didn't find any trailing `>` characters, then we have nothing to error about.
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally, double check that we have our end token as otherwise this is the
|
|
||||||
// second case.
|
|
||||||
if self.look_ahead(position, |t| {
|
|
||||||
trace!("check_trailing_angle_brackets: t={:?}", t);
|
|
||||||
*t == end
|
|
||||||
}) {
|
|
||||||
// Eat from where we started until the end token so that parsing can continue
|
|
||||||
// as if we didn't have those extra angle brackets.
|
|
||||||
self.eat_to_tokens(&[&end]);
|
|
||||||
let span = lo.until(self.span);
|
|
||||||
|
|
||||||
let plural = number_of_gt > 1 || number_of_shr >= 1;
|
|
||||||
self.diagnostic()
|
|
||||||
.struct_span_err(
|
|
||||||
span,
|
|
||||||
&format!("unmatched angle bracket{}", if plural { "s" } else { "" }),
|
|
||||||
)
|
|
||||||
.span_suggestion(
|
|
||||||
span,
|
|
||||||
&format!("remove extra angle bracket{}", if plural { "s" } else { "" }),
|
|
||||||
String::new(),
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
)
|
|
||||||
.emit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_dot_or_call_expr_with_(&mut self, e0: P<Expr>, lo: Span) -> PResult<'a, P<Expr>> {
|
fn parse_dot_or_call_expr_with_(&mut self, e0: P<Expr>, lo: Span) -> PResult<'a, P<Expr>> {
|
||||||
let mut e = e0;
|
let mut e = e0;
|
||||||
let mut hi;
|
let mut hi;
|
||||||
|
@ -3529,33 +3194,6 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Produce an error if comparison operators are chained (RFC #558).
|
|
||||||
/// We only need to check lhs, not rhs, because all comparison ops
|
|
||||||
/// have same precedence and are left-associative
|
|
||||||
fn check_no_chained_comparison(&self, lhs: &Expr, outer_op: &AssocOp) {
|
|
||||||
debug_assert!(outer_op.is_comparison(),
|
|
||||||
"check_no_chained_comparison: {:?} is not comparison",
|
|
||||||
outer_op);
|
|
||||||
match lhs.node {
|
|
||||||
ExprKind::Binary(op, _, _) if op.node.is_comparison() => {
|
|
||||||
// respan to include both operators
|
|
||||||
let op_span = op.span.to(self.span);
|
|
||||||
let mut err = self.diagnostic().struct_span_err(op_span,
|
|
||||||
"chained comparison operators require parentheses");
|
|
||||||
if op.node == BinOpKind::Lt &&
|
|
||||||
*outer_op == AssocOp::Less || // Include `<` to provide this recommendation
|
|
||||||
*outer_op == AssocOp::Greater // even in a case like the following:
|
|
||||||
{ // Foo<Bar<Baz<Qux, ()>>>
|
|
||||||
err.help(
|
|
||||||
"use `::<...>` instead of `<...>` if you meant to specify type arguments");
|
|
||||||
err.help("or use `(...)` if you meant to specify fn arguments");
|
|
||||||
}
|
|
||||||
err.emit();
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse prefix-forms of range notation: `..expr`, `..`, `..=expr`
|
/// Parse prefix-forms of range notation: `..expr`, `..`, `..=expr`
|
||||||
fn parse_prefix_range_expr(&mut self,
|
fn parse_prefix_range_expr(&mut self,
|
||||||
already_parsed_attrs: Option<ThinVec<Attribute>>)
|
already_parsed_attrs: Option<ThinVec<Attribute>>)
|
||||||
|
@ -3732,20 +3370,7 @@ impl<'a> Parser<'a> {
|
||||||
err.emit();
|
err.emit();
|
||||||
}
|
}
|
||||||
let in_span = self.prev_span;
|
let in_span = self.prev_span;
|
||||||
if self.eat_keyword(kw::In) {
|
self.check_for_for_in_in_typo(in_span);
|
||||||
// a common typo: `for _ in in bar {}`
|
|
||||||
let mut err = self.sess.span_diagnostic.struct_span_err(
|
|
||||||
self.prev_span,
|
|
||||||
"expected iterable, found keyword `in`",
|
|
||||||
);
|
|
||||||
err.span_suggestion_short(
|
|
||||||
in_span.until(self.prev_span),
|
|
||||||
"remove the duplicated `in`",
|
|
||||||
String::new(),
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
err.emit();
|
|
||||||
}
|
|
||||||
let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
|
let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
|
||||||
let (iattrs, loop_block) = self.parse_inner_attrs_and_block()?;
|
let (iattrs, loop_block) = self.parse_inner_attrs_and_block()?;
|
||||||
attrs.extend(iattrs);
|
attrs.extend(iattrs);
|
||||||
|
@ -6327,7 +5952,9 @@ impl<'a> Parser<'a> {
|
||||||
let (constness, unsafety, mut asyncness, abi) = self.parse_fn_front_matter()?;
|
let (constness, unsafety, mut asyncness, abi) = self.parse_fn_front_matter()?;
|
||||||
let ident = self.parse_ident()?;
|
let ident = self.parse_ident()?;
|
||||||
let mut generics = self.parse_generics()?;
|
let mut generics = self.parse_generics()?;
|
||||||
let mut decl = self.parse_fn_decl_with_self(|p| p.parse_arg())?;
|
let mut decl = self.parse_fn_decl_with_self(|p| {
|
||||||
|
p.parse_arg_general(true, true, false)
|
||||||
|
})?;
|
||||||
generics.where_clause = self.parse_where_clause()?;
|
generics.where_clause = self.parse_where_clause()?;
|
||||||
self.construct_async_arguments(&mut asyncness, &mut decl);
|
self.construct_async_arguments(&mut asyncness, &mut decl);
|
||||||
*at_end = true;
|
*at_end = true;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue