Suggest expressions that look like const generic arguments should be enclosed in brackets
Co-Authored-By: Esteban Kuber <github@kuber.com.ar>
This commit is contained in:
parent
1d2726726f
commit
ac1454001c
19 changed files with 782 additions and 35 deletions
|
@ -1,13 +1,14 @@
|
|||
use super::ty::AllowPlus;
|
||||
use super::{BlockMode, Parser, PathStyle, SemiColonMode, SeqSep, TokenExpectType, TokenType};
|
||||
use super::TokenType;
|
||||
use super::{BlockMode, Parser, PathStyle, Restrictions, SemiColonMode, SeqSep, TokenExpectType};
|
||||
|
||||
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, Block, BlockCheckMode, Expr,
|
||||
ExprKind, Item, ItemKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QSelf, Ty,
|
||||
TyKind,
|
||||
self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode,
|
||||
Block, BlockCheckMode, Expr, ExprKind, GenericArg, Item, ItemKind, Mutability, Param, Pat,
|
||||
PatKind, Path, PathSegment, QSelf, Ty, TyKind,
|
||||
};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
|
@ -1774,4 +1775,142 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle encountering a symbol in a generic argument list that is not a `,` or `>`. In this
|
||||
/// case, we emit an error and try to suggest enclosing a const argument in braces if it looks
|
||||
/// like the user has forgotten them.
|
||||
pub fn handle_ambiguous_unbraced_const_arg(
|
||||
&mut self,
|
||||
args: &mut Vec<AngleBracketedArg>,
|
||||
) -> PResult<'a, bool> {
|
||||
// If we haven't encountered a closing `>`, then the argument is malformed.
|
||||
// It's likely that the user has written a const expression without enclosing it
|
||||
// in braces, so we try to recover here.
|
||||
let arg = args.pop().unwrap();
|
||||
// FIXME: for some reason using `unexpected` or `expected_one_of_not_found` has
|
||||
// adverse side-effects to subsequent errors and seems to advance the parser.
|
||||
// We are causing this error here exclusively in case that a `const` expression
|
||||
// could be recovered from the current parser state, even if followed by more
|
||||
// arguments after a comma.
|
||||
let mut err = self.struct_span_err(
|
||||
self.token.span,
|
||||
&format!("expected one of `,` or `>`, found {}", super::token_descr(&self.token)),
|
||||
);
|
||||
err.span_label(self.token.span, "expected one of `,` or `>`");
|
||||
match self.recover_const_arg(arg.span(), err) {
|
||||
Ok(arg) => {
|
||||
args.push(AngleBracketedArg::Arg(arg));
|
||||
if self.eat(&token::Comma) {
|
||||
return Ok(true); // Continue
|
||||
}
|
||||
}
|
||||
Err(mut err) => {
|
||||
args.push(arg);
|
||||
// We will emit a more generic error later.
|
||||
err.delay_as_bug();
|
||||
}
|
||||
}
|
||||
return Ok(false); // Don't continue.
|
||||
}
|
||||
|
||||
/// Handle a generic const argument that had not been enclosed in braces, and suggest enclosing
|
||||
/// it braces. In this situation, unlike in `handle_ambiguous_unbraced_const_arg`, this is
|
||||
/// almost certainly a const argument, so we always offer a suggestion.
|
||||
pub fn handle_unambiguous_unbraced_const_arg(&mut self) -> PResult<'a, P<Expr>> {
|
||||
let start = self.token.span;
|
||||
let expr = self.parse_expr_res(Restrictions::CONST_EXPR, None).map_err(|mut err| {
|
||||
err.span_label(
|
||||
start.shrink_to_lo(),
|
||||
"while parsing a const generic argument starting here",
|
||||
);
|
||||
err
|
||||
})?;
|
||||
if !self.expr_is_valid_const_arg(&expr) {
|
||||
self.struct_span_err(
|
||||
expr.span,
|
||||
"expressions must be enclosed in braces to be used as const generic \
|
||||
arguments",
|
||||
)
|
||||
.multipart_suggestion(
|
||||
"enclose the `const` expression in braces",
|
||||
vec![
|
||||
(expr.span.shrink_to_lo(), "{ ".to_string()),
|
||||
(expr.span.shrink_to_hi(), " }".to_string()),
|
||||
],
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
Ok(expr)
|
||||
}
|
||||
|
||||
/// Try to recover from possible generic const argument without `{` and `}`.
|
||||
///
|
||||
/// When encountering code like `foo::< bar + 3 >` or `foo::< bar - baz >` we suggest
|
||||
/// `foo::<{ bar + 3 }>` and `foo::<{ bar - baz }>`, respectively. We only provide a suggestion
|
||||
/// if we think that that the resulting expression would be well formed.
|
||||
pub fn recover_const_arg(
|
||||
&mut self,
|
||||
start: Span,
|
||||
mut err: DiagnosticBuilder<'a>,
|
||||
) -> PResult<'a, GenericArg> {
|
||||
let is_op = AssocOp::from_token(&self.token)
|
||||
.and_then(|op| {
|
||||
if let AssocOp::Greater
|
||||
| AssocOp::Less
|
||||
| AssocOp::ShiftRight
|
||||
| AssocOp::GreaterEqual
|
||||
// Don't recover from `foo::<bar = baz>`, because this could be an attempt to
|
||||
// assign a value to a defaulted generic parameter.
|
||||
| AssocOp::Assign
|
||||
| AssocOp::AssignOp(_) = op
|
||||
{
|
||||
None
|
||||
} else {
|
||||
Some(op)
|
||||
}
|
||||
})
|
||||
.is_some();
|
||||
// This will be true when a trait object type `Foo +` or a path which was a `const fn` with
|
||||
// type params has been parsed.
|
||||
let was_op =
|
||||
matches!(self.prev_token.kind, token::BinOp(token::Plus | token::Shr) | token::Gt);
|
||||
if !is_op && !was_op {
|
||||
// We perform these checks and early return to avoid taking a snapshot unnecessarily.
|
||||
return Err(err);
|
||||
}
|
||||
let snapshot = self.clone();
|
||||
if is_op {
|
||||
self.bump();
|
||||
}
|
||||
match self.parse_expr_res(Restrictions::CONST_EXPR, None) {
|
||||
Ok(expr) => {
|
||||
if token::Comma == self.token.kind || self.token.kind.should_end_const_arg() {
|
||||
// Avoid the following output by checking that we consumed a full const arg:
|
||||
// help: expressions must be enclosed in braces to be used as const generic
|
||||
// arguments
|
||||
// |
|
||||
// LL | let sr: Vec<{ (u32, _, _) = vec![] };
|
||||
// | ^ ^
|
||||
err.multipart_suggestion(
|
||||
"expressions must be enclosed in braces to be used as const generic \
|
||||
arguments",
|
||||
vec![
|
||||
(start.shrink_to_lo(), "{ ".to_string()),
|
||||
(expr.span.shrink_to_hi(), " }".to_string()),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
let value = self.mk_expr_err(start.to(expr.span));
|
||||
err.emit();
|
||||
return Ok(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value }));
|
||||
}
|
||||
}
|
||||
Err(mut err) => {
|
||||
err.cancel();
|
||||
}
|
||||
}
|
||||
*self = snapshot;
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue