Rollup merge of #139392 - compiler-errors:raw-expr, r=oli-obk
Detect and provide suggestion for `&raw EXPR` When emitting an error in the parser, and we detect that the previous token was `raw` and we *could* have consumed `const`/`mut`, suggest that this may have been a mistyped raw ref expr. To do this, we add `const`/`mut` to the expected token set when parsing `&raw` as an expression (which does not affect the "good path" of parsing, for the record). This is kind of a rudimentary error improvement, since it doesn't actually attempt to recover anything, leading to some other knock-on errors b/c we still treat `&raw` as the expression that was parsed... but at least we add the suggestion! I don't think the parser grammar means we can faithfully recover `&raw EXPR` early, i.e. during `parse_expr_borrow`. Fixes #133231
This commit is contained in:
commit
bf49dfc943
6 changed files with 189 additions and 1 deletions
|
@ -120,6 +120,17 @@ impl Path {
|
||||||
Path { segments: thin_vec![PathSegment::from_ident(ident)], span: ident.span, tokens: None }
|
Path { segments: thin_vec![PathSegment::from_ident(ident)], span: ident.span, tokens: None }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_ident(&self, name: Symbol) -> bool {
|
||||||
|
if let [segment] = self.segments.as_ref()
|
||||||
|
&& segment.args.is_none()
|
||||||
|
&& segment.ident.name == name
|
||||||
|
{
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_global(&self) -> bool {
|
pub fn is_global(&self) -> bool {
|
||||||
self.segments.first().is_some_and(|segment| segment.ident.name == kw::PathRoot)
|
self.segments.first().is_some_and(|segment| segment.ident.name == kw::PathRoot)
|
||||||
}
|
}
|
||||||
|
|
|
@ -609,6 +609,8 @@ impl<'a> Parser<'a> {
|
||||||
// FIXME: translation requires list formatting (for `expect`)
|
// FIXME: translation requires list formatting (for `expect`)
|
||||||
let mut err = self.dcx().struct_span_err(self.token.span, msg_exp);
|
let mut err = self.dcx().struct_span_err(self.token.span, msg_exp);
|
||||||
|
|
||||||
|
self.label_expected_raw_ref(&mut err);
|
||||||
|
|
||||||
// Look for usages of '=>' where '>=' was probably intended
|
// Look for usages of '=>' where '>=' was probably intended
|
||||||
if self.token == token::FatArrow
|
if self.token == token::FatArrow
|
||||||
&& expected.iter().any(|tok| matches!(tok, TokenType::Operator | TokenType::Le))
|
&& expected.iter().any(|tok| matches!(tok, TokenType::Operator | TokenType::Le))
|
||||||
|
@ -750,6 +752,25 @@ impl<'a> Parser<'a> {
|
||||||
Err(err)
|
Err(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a label when `&raw EXPR` was written instead of `&raw const EXPR`/`&raw mut EXPR`.
|
||||||
|
///
|
||||||
|
/// Given that not all parser diagnostics flow through `expected_one_of_not_found`, this
|
||||||
|
/// label may need added to other diagnostics emission paths as needed.
|
||||||
|
pub(super) fn label_expected_raw_ref(&mut self, err: &mut Diag<'_>) {
|
||||||
|
if self.prev_token.is_keyword(kw::Raw)
|
||||||
|
&& self.expected_token_types.contains(TokenType::KwMut)
|
||||||
|
&& self.expected_token_types.contains(TokenType::KwConst)
|
||||||
|
&& self.token.can_begin_expr()
|
||||||
|
{
|
||||||
|
err.span_suggestions(
|
||||||
|
self.prev_token.span.shrink_to_hi(),
|
||||||
|
"`&raw` must be followed by `const` or `mut` to be a raw reference expression",
|
||||||
|
[" const".to_string(), " mut".to_string()],
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks if the current token or the previous token are misspelled keywords
|
/// Checks if the current token or the previous token are misspelled keywords
|
||||||
/// and adds a helpful suggestion.
|
/// and adds a helpful suggestion.
|
||||||
fn check_for_misspelled_kw(&self, err: &mut Diag<'_>, expected: &[TokenType]) {
|
fn check_for_misspelled_kw(&self, err: &mut Diag<'_>, expected: &[TokenType]) {
|
||||||
|
|
|
@ -827,6 +827,18 @@ impl<'a> Parser<'a> {
|
||||||
if let Some(lt) = lifetime {
|
if let Some(lt) = lifetime {
|
||||||
self.error_remove_borrow_lifetime(span, lt.ident.span.until(expr.span));
|
self.error_remove_borrow_lifetime(span, lt.ident.span.until(expr.span));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add expected tokens if we parsed `&raw` as an expression.
|
||||||
|
// This will make sure we see "expected `const`, `mut`", and
|
||||||
|
// guides recovery in case we write `&raw expr`.
|
||||||
|
if borrow_kind == ast::BorrowKind::Ref
|
||||||
|
&& mutbl == ast::Mutability::Not
|
||||||
|
&& matches!(&expr.kind, ExprKind::Path(None, p) if p.is_ident(kw::Raw))
|
||||||
|
{
|
||||||
|
self.expected_token_types.insert(TokenType::KwMut);
|
||||||
|
self.expected_token_types.insert(TokenType::KwConst);
|
||||||
|
}
|
||||||
|
|
||||||
Ok((span, ExprKind::AddrOf(borrow_kind, mutbl, expr)))
|
Ok((span, ExprKind::AddrOf(borrow_kind, mutbl, expr)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -518,7 +518,11 @@ impl<'a> Parser<'a> {
|
||||||
let prev = self.prev_token.span;
|
let prev = self.prev_token.span;
|
||||||
let sp = self.token.span;
|
let sp = self.token.span;
|
||||||
let mut e = self.dcx().struct_span_err(sp, msg);
|
let mut e = self.dcx().struct_span_err(sp, msg);
|
||||||
let do_not_suggest_help = self.token.is_keyword(kw::In) || self.token == token::Colon;
|
self.label_expected_raw_ref(&mut e);
|
||||||
|
|
||||||
|
let do_not_suggest_help = self.token.is_keyword(kw::In)
|
||||||
|
|| self.token == token::Colon
|
||||||
|
|| self.prev_token.is_keyword(kw::Raw);
|
||||||
|
|
||||||
// Check to see if the user has written something like
|
// Check to see if the user has written something like
|
||||||
//
|
//
|
||||||
|
|
31
tests/ui/parser/recover/raw-no-const-mut.rs
Normal file
31
tests/ui/parser/recover/raw-no-const-mut.rs
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
fn a() {
|
||||||
|
let x = &raw 1;
|
||||||
|
//~^ ERROR expected one of
|
||||||
|
}
|
||||||
|
|
||||||
|
fn b() {
|
||||||
|
[&raw const 1, &raw 2]
|
||||||
|
//~^ ERROR expected one of
|
||||||
|
//~| ERROR cannot find value `raw` in this scope
|
||||||
|
//~| ERROR cannot take address of a temporary
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c() {
|
||||||
|
if x == &raw z {}
|
||||||
|
//~^ ERROR expected `{`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn d() {
|
||||||
|
f(&raw 2);
|
||||||
|
//~^ ERROR expected one of
|
||||||
|
//~| ERROR cannot find value `raw` in this scope
|
||||||
|
//~| ERROR cannot find function `f` in this scope
|
||||||
|
}
|
||||||
|
|
||||||
|
fn e() {
|
||||||
|
let x;
|
||||||
|
x = &raw 1;
|
||||||
|
//~^ ERROR expected one of
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
109
tests/ui/parser/recover/raw-no-const-mut.stderr
Normal file
109
tests/ui/parser/recover/raw-no-const-mut.stderr
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
error: expected one of `!`, `.`, `::`, `;`, `?`, `const`, `else`, `mut`, `{`, or an operator, found `1`
|
||||||
|
--> $DIR/raw-no-const-mut.rs:2:18
|
||||||
|
|
|
||||||
|
LL | let x = &raw 1;
|
||||||
|
| ^ expected one of 10 possible tokens
|
||||||
|
|
|
||||||
|
help: `&raw` must be followed by `const` or `mut` to be a raw reference expression
|
||||||
|
|
|
||||||
|
LL | let x = &raw const 1;
|
||||||
|
| +++++
|
||||||
|
LL | let x = &raw mut 1;
|
||||||
|
| +++
|
||||||
|
|
||||||
|
error: expected one of `!`, `,`, `.`, `::`, `?`, `]`, `const`, `mut`, `{`, or an operator, found `2`
|
||||||
|
--> $DIR/raw-no-const-mut.rs:7:25
|
||||||
|
|
|
||||||
|
LL | [&raw const 1, &raw 2]
|
||||||
|
| ^ expected one of 10 possible tokens
|
||||||
|
|
|
||||||
|
help: `&raw` must be followed by `const` or `mut` to be a raw reference expression
|
||||||
|
|
|
||||||
|
LL | [&raw const 1, &raw const 2]
|
||||||
|
| +++++
|
||||||
|
LL | [&raw const 1, &raw mut 2]
|
||||||
|
| +++
|
||||||
|
help: missing `,`
|
||||||
|
|
|
||||||
|
LL | [&raw const 1, &raw, 2]
|
||||||
|
| +
|
||||||
|
|
||||||
|
error: expected `{`, found `z`
|
||||||
|
--> $DIR/raw-no-const-mut.rs:14:18
|
||||||
|
|
|
||||||
|
LL | if x == &raw z {}
|
||||||
|
| ^ expected `{`
|
||||||
|
|
|
||||||
|
note: the `if` expression is missing a block after this condition
|
||||||
|
--> $DIR/raw-no-const-mut.rs:14:8
|
||||||
|
|
|
||||||
|
LL | if x == &raw z {}
|
||||||
|
| ^^^^^^^^^
|
||||||
|
help: `&raw` must be followed by `const` or `mut` to be a raw reference expression
|
||||||
|
|
|
||||||
|
LL | if x == &raw const z {}
|
||||||
|
| +++++
|
||||||
|
LL | if x == &raw mut z {}
|
||||||
|
| +++
|
||||||
|
|
||||||
|
error: expected one of `!`, `)`, `,`, `.`, `::`, `?`, `const`, `mut`, `{`, or an operator, found `2`
|
||||||
|
--> $DIR/raw-no-const-mut.rs:19:12
|
||||||
|
|
|
||||||
|
LL | f(&raw 2);
|
||||||
|
| ^ expected one of 10 possible tokens
|
||||||
|
|
|
||||||
|
help: `&raw` must be followed by `const` or `mut` to be a raw reference expression
|
||||||
|
|
|
||||||
|
LL | f(&raw const 2);
|
||||||
|
| +++++
|
||||||
|
LL | f(&raw mut 2);
|
||||||
|
| +++
|
||||||
|
help: missing `,`
|
||||||
|
|
|
||||||
|
LL | f(&raw, 2);
|
||||||
|
| +
|
||||||
|
|
||||||
|
error: expected one of `!`, `.`, `::`, `;`, `?`, `const`, `mut`, `{`, `}`, or an operator, found `1`
|
||||||
|
--> $DIR/raw-no-const-mut.rs:27:14
|
||||||
|
|
|
||||||
|
LL | x = &raw 1;
|
||||||
|
| ^ expected one of 10 possible tokens
|
||||||
|
|
|
||||||
|
help: `&raw` must be followed by `const` or `mut` to be a raw reference expression
|
||||||
|
|
|
||||||
|
LL | x = &raw const 1;
|
||||||
|
| +++++
|
||||||
|
LL | x = &raw mut 1;
|
||||||
|
| +++
|
||||||
|
|
||||||
|
error[E0425]: cannot find value `raw` in this scope
|
||||||
|
--> $DIR/raw-no-const-mut.rs:7:21
|
||||||
|
|
|
||||||
|
LL | [&raw const 1, &raw 2]
|
||||||
|
| ^^^ not found in this scope
|
||||||
|
|
||||||
|
error[E0425]: cannot find value `raw` in this scope
|
||||||
|
--> $DIR/raw-no-const-mut.rs:19:8
|
||||||
|
|
|
||||||
|
LL | f(&raw 2);
|
||||||
|
| ^^^ not found in this scope
|
||||||
|
|
||||||
|
error[E0745]: cannot take address of a temporary
|
||||||
|
--> $DIR/raw-no-const-mut.rs:7:17
|
||||||
|
|
|
||||||
|
LL | [&raw const 1, &raw 2]
|
||||||
|
| ^ temporary value
|
||||||
|
|
||||||
|
error[E0425]: cannot find function `f` in this scope
|
||||||
|
--> $DIR/raw-no-const-mut.rs:19:5
|
||||||
|
|
|
||||||
|
LL | fn a() {
|
||||||
|
| ------ similarly named function `a` defined here
|
||||||
|
...
|
||||||
|
LL | f(&raw 2);
|
||||||
|
| ^ help: a function with a similar name exists: `a`
|
||||||
|
|
||||||
|
error: aborting due to 9 previous errors
|
||||||
|
|
||||||
|
Some errors have detailed explanations: E0425, E0745.
|
||||||
|
For more information about an error, try `rustc --explain E0425`.
|
Loading…
Add table
Add a link
Reference in a new issue