Skip to content

Commit 6dfbe7c

Browse files
Detect and provide suggestion for &raw EXPR
1 parent 82eb03e commit 6dfbe7c

File tree

6 files changed

+189
-1
lines changed

6 files changed

+189
-1
lines changed

compiler/rustc_ast/src/ast.rs

+11
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,17 @@ impl Path {
120120
Path { segments: thin_vec![PathSegment::from_ident(ident)], span: ident.span, tokens: None }
121121
}
122122

123+
pub fn is_ident(&self, name: Symbol) -> bool {
124+
if let [segment] = self.segments.as_ref()
125+
&& segment.args.is_none()
126+
&& segment.ident.name == name
127+
{
128+
true
129+
} else {
130+
false
131+
}
132+
}
133+
123134
pub fn is_global(&self) -> bool {
124135
self.segments.first().is_some_and(|segment| segment.ident.name == kw::PathRoot)
125136
}

compiler/rustc_parse/src/parser/diagnostics.rs

+21
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,8 @@ impl<'a> Parser<'a> {
609609
// FIXME: translation requires list formatting (for `expect`)
610610
let mut err = self.dcx().struct_span_err(self.token.span, msg_exp);
611611

612+
self.label_expected_raw_ref(&mut err);
613+
612614
// Look for usages of '=>' where '>=' was probably intended
613615
if self.token == token::FatArrow
614616
&& expected.iter().any(|tok| matches!(tok, TokenType::Operator | TokenType::Le))
@@ -750,6 +752,25 @@ impl<'a> Parser<'a> {
750752
Err(err)
751753
}
752754

755+
/// Adds a label when `&raw EXPR` was written instead of `&raw const EXPR`/`&raw mut EXPR`.
756+
///
757+
/// Given that not all parser diagnostics flow through `expected_one_of_not_found`, this
758+
/// label may need added to other diagnostics emission paths as needed.
759+
pub(super) fn label_expected_raw_ref(&mut self, err: &mut Diag<'_>) {
760+
if self.prev_token.is_keyword(kw::Raw)
761+
&& self.expected_token_types.contains(TokenType::KwMut)
762+
&& self.expected_token_types.contains(TokenType::KwConst)
763+
&& self.token.can_begin_expr()
764+
{
765+
err.span_suggestions(
766+
self.prev_token.span.shrink_to_hi(),
767+
"`&raw` must be followed by `const` or `mut` to be a raw reference expression",
768+
[" const".to_string(), " mut".to_string()],
769+
Applicability::MaybeIncorrect,
770+
);
771+
}
772+
}
773+
753774
/// Checks if the current token or the previous token are misspelled keywords
754775
/// and adds a helpful suggestion.
755776
fn check_for_misspelled_kw(&self, err: &mut Diag<'_>, expected: &[TokenType]) {

compiler/rustc_parse/src/parser/expr.rs

+12
Original file line numberDiff line numberDiff line change
@@ -829,6 +829,18 @@ impl<'a> Parser<'a> {
829829
if let Some(lt) = lifetime {
830830
self.error_remove_borrow_lifetime(span, lt.ident.span.until(expr.span));
831831
}
832+
833+
// Add expected tokens if we parsed `&raw` as an expression.
834+
// This will make sure we see "expected `const`, `mut`", and
835+
// guides recovery in case we write `&raw expr`.
836+
if borrow_kind == ast::BorrowKind::Ref
837+
&& mutbl == ast::Mutability::Not
838+
&& matches!(&expr.kind, ExprKind::Path(None, p) if p.is_ident(kw::Raw))
839+
{
840+
self.expected_token_types.insert(TokenType::KwMut);
841+
self.expected_token_types.insert(TokenType::KwConst);
842+
}
843+
832844
Ok((span, ExprKind::AddrOf(borrow_kind, mutbl, expr)))
833845
}
834846

compiler/rustc_parse/src/parser/stmt.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,11 @@ impl<'a> Parser<'a> {
516516
let prev = self.prev_token.span;
517517
let sp = self.token.span;
518518
let mut e = self.dcx().struct_span_err(sp, msg);
519-
let do_not_suggest_help = self.token.is_keyword(kw::In) || self.token == token::Colon;
519+
self.label_expected_raw_ref(&mut e);
520+
521+
let do_not_suggest_help = self.token.is_keyword(kw::In)
522+
|| self.token == token::Colon
523+
|| self.prev_token.is_keyword(kw::Raw);
520524

521525
// Check to see if the user has written something like
522526
//
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
fn a() {
2+
let x = &raw 1;
3+
//~^ ERROR expected one of
4+
}
5+
6+
fn b() {
7+
[&raw const 1, &raw 2]
8+
//~^ ERROR expected one of
9+
//~| ERROR cannot find value `raw` in this scope
10+
//~| ERROR cannot take address of a temporary
11+
}
12+
13+
fn c() {
14+
if x == &raw z {}
15+
//~^ ERROR expected `{`
16+
}
17+
18+
fn d() {
19+
f(&raw 2);
20+
//~^ ERROR expected one of
21+
//~| ERROR cannot find value `raw` in this scope
22+
//~| ERROR cannot find function `f` in this scope
23+
}
24+
25+
fn e() {
26+
let x;
27+
x = &raw 1;
28+
//~^ ERROR expected one of
29+
}
30+
31+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
error: expected one of `!`, `.`, `::`, `;`, `?`, `const`, `else`, `mut`, `{`, or an operator, found `1`
2+
--> $DIR/raw-no-const-mut.rs:2:18
3+
|
4+
LL | let x = &raw 1;
5+
| ^ expected one of 10 possible tokens
6+
|
7+
help: `&raw` must be followed by `const` or `mut` to be a raw reference expression
8+
|
9+
LL | let x = &raw const 1;
10+
| +++++
11+
LL | let x = &raw mut 1;
12+
| +++
13+
14+
error: expected one of `!`, `,`, `.`, `::`, `?`, `]`, `const`, `mut`, `{`, or an operator, found `2`
15+
--> $DIR/raw-no-const-mut.rs:7:25
16+
|
17+
LL | [&raw const 1, &raw 2]
18+
| ^ expected one of 10 possible tokens
19+
|
20+
help: `&raw` must be followed by `const` or `mut` to be a raw reference expression
21+
|
22+
LL | [&raw const 1, &raw const 2]
23+
| +++++
24+
LL | [&raw const 1, &raw mut 2]
25+
| +++
26+
help: missing `,`
27+
|
28+
LL | [&raw const 1, &raw, 2]
29+
| +
30+
31+
error: expected `{`, found `z`
32+
--> $DIR/raw-no-const-mut.rs:14:18
33+
|
34+
LL | if x == &raw z {}
35+
| ^ expected `{`
36+
|
37+
note: the `if` expression is missing a block after this condition
38+
--> $DIR/raw-no-const-mut.rs:14:8
39+
|
40+
LL | if x == &raw z {}
41+
| ^^^^^^^^^
42+
help: `&raw` must be followed by `const` or `mut` to be a raw reference expression
43+
|
44+
LL | if x == &raw const z {}
45+
| +++++
46+
LL | if x == &raw mut z {}
47+
| +++
48+
49+
error: expected one of `!`, `)`, `,`, `.`, `::`, `?`, `const`, `mut`, `{`, or an operator, found `2`
50+
--> $DIR/raw-no-const-mut.rs:19:12
51+
|
52+
LL | f(&raw 2);
53+
| ^ expected one of 10 possible tokens
54+
|
55+
help: `&raw` must be followed by `const` or `mut` to be a raw reference expression
56+
|
57+
LL | f(&raw const 2);
58+
| +++++
59+
LL | f(&raw mut 2);
60+
| +++
61+
help: missing `,`
62+
|
63+
LL | f(&raw, 2);
64+
| +
65+
66+
error: expected one of `!`, `.`, `::`, `;`, `?`, `const`, `mut`, `{`, `}`, or an operator, found `1`
67+
--> $DIR/raw-no-const-mut.rs:27:14
68+
|
69+
LL | x = &raw 1;
70+
| ^ expected one of 10 possible tokens
71+
|
72+
help: `&raw` must be followed by `const` or `mut` to be a raw reference expression
73+
|
74+
LL | x = &raw const 1;
75+
| +++++
76+
LL | x = &raw mut 1;
77+
| +++
78+
79+
error[E0425]: cannot find value `raw` in this scope
80+
--> $DIR/raw-no-const-mut.rs:7:21
81+
|
82+
LL | [&raw const 1, &raw 2]
83+
| ^^^ not found in this scope
84+
85+
error[E0425]: cannot find value `raw` in this scope
86+
--> $DIR/raw-no-const-mut.rs:19:8
87+
|
88+
LL | f(&raw 2);
89+
| ^^^ not found in this scope
90+
91+
error[E0745]: cannot take address of a temporary
92+
--> $DIR/raw-no-const-mut.rs:7:17
93+
|
94+
LL | [&raw const 1, &raw 2]
95+
| ^ temporary value
96+
97+
error[E0425]: cannot find function `f` in this scope
98+
--> $DIR/raw-no-const-mut.rs:19:5
99+
|
100+
LL | fn a() {
101+
| ------ similarly named function `a` defined here
102+
...
103+
LL | f(&raw 2);
104+
| ^ help: a function with a similar name exists: `a`
105+
106+
error: aborting due to 9 previous errors
107+
108+
Some errors have detailed explanations: E0425, E0745.
109+
For more information about an error, try `rustc --explain E0425`.

0 commit comments

Comments
 (0)