Skip to content

Commit 9a129e5

Browse files
committed
improve parse item fallback
1 parent 91c0823 commit 9a129e5

20 files changed

+132
-57
lines changed

compiler/rustc_ast/src/tokenstream.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -661,11 +661,11 @@ impl TokenStream {
661661
if attr_style == AttrStyle::Inner {
662662
vec![
663663
TokenTree::token_joint(token::Pound, span),
664-
TokenTree::token_joint_hidden(token::Not, span),
664+
TokenTree::token_alone(token::Not, span),
665665
body,
666666
]
667667
} else {
668-
vec![TokenTree::token_joint_hidden(token::Pound, span), body]
668+
vec![TokenTree::token_alone(token::Pound, span), body]
669669
}
670670
}
671671
}

compiler/rustc_ast_pretty/src/pprust/mod.rs

+8
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,14 @@ pub fn attribute_to_string(attr: &ast::Attribute) -> String {
7676
State::new().attribute_to_string(attr)
7777
}
7878

79+
pub fn local_to_string(local: &ast::Local) -> String {
80+
State::new().local_to_string(local)
81+
}
82+
83+
pub fn stmt_to_string(stmt: &ast::Stmt) -> String {
84+
State::new().stmt_to_string(stmt)
85+
}
86+
7987
pub fn to_string(f: impl FnOnce(&mut State<'_>)) -> String {
8088
State::to_string(f)
8189
}

compiler/rustc_ast_pretty/src/pprust/state.rs

+4
Original file line numberDiff line numberDiff line change
@@ -2075,4 +2075,8 @@ impl<'a> State<'a> {
20752075
pub(crate) fn attribute_to_string(&self, attr: &ast::Attribute) -> String {
20762076
Self::to_string(|s| s.print_attribute(attr))
20772077
}
2078+
2079+
pub(crate) fn local_to_string(&self, local: &ast::Local) -> String {
2080+
Self::to_string(|s| s.print_local_decl(local))
2081+
}
20782082
}

compiler/rustc_parse/src/parser/attr_wrapper.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use std::ops::Range;
1515
/// for the attribute target. This allows us to perform cfg-expansion on
1616
/// a token stream before we invoke a derive proc-macro.
1717
///
18-
/// This wrapper prevents direct access to the underlying `ast::AttrVec>`.
18+
/// This wrapper prevents direct access to the underlying `ast::AttrVec`.
1919
/// Parsing code can only get access to the underlying attributes
2020
/// by passing an `AttrWrapper` to `collect_tokens_trailing_tokens`.
2121
/// This makes it difficult to accidentally construct an AST node

compiler/rustc_parse/src/parser/item.rs

+85-19
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use super::diagnostics::{dummy_arg, ConsumeClosingDelim};
22
use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
33
use super::{
4-
AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle, Trailing, TrailingToken,
4+
AttemptLocalParseRecovery, AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle,
5+
Trailing, TrailingToken,
56
};
67
use crate::errors::{self, MacroExpandsToAdtField};
78
use crate::fluent_generated as fluent;
@@ -40,6 +41,7 @@ impl<'a> Parser<'a> {
4041
let mod_kind = if self.eat(&token::Semi) {
4142
ModKind::Unloaded
4243
} else {
44+
// mod blocks are parsed recursively
4345
self.expect(&token::OpenDelim(Delimiter::Brace))?;
4446
let (inner_attrs, items, inner_span) =
4547
self.parse_mod(&token::CloseDelim(Delimiter::Brace))?;
@@ -72,28 +74,84 @@ impl<'a> Parser<'a> {
7274
}
7375

7476
if !self.eat(term) {
75-
let token_str = super::token_descr(&self.token);
77+
// The last token should be `term`: either EOF or `}`. If it's not that means that we've had an error
78+
// parsing an item
7679
if !self.maybe_consume_incorrect_semicolon(items.last().map(|x| &**x)) {
77-
let msg = format!("expected item, found {token_str}");
78-
let mut err = self.dcx().struct_span_err(self.token.span, msg);
79-
let span = self.token.span;
80-
if self.is_kw_followed_by_ident(kw::Let) {
81-
err.span_label(
82-
span,
83-
"consider using `const` or `static` instead of `let` for global variables",
84-
);
85-
} else {
86-
err.span_label(span, "expected item")
87-
.note("for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>");
88-
};
89-
return Err(err);
80+
self.fallback_incorrect_item()?;
9081
}
9182
}
9283

9384
let inject_use_span = post_attr_lo.data().with_hi(post_attr_lo.lo());
9485
let mod_spans = ModSpans { inner_span: lo.to(self.prev_token.span), inject_use_span };
9586
Ok((attrs, items, mod_spans))
9687
}
88+
89+
fn fallback_incorrect_item(&mut self) -> PResult<'a, ()> {
90+
let token_str = super::token_descr(&self.token);
91+
let mut err = self
92+
.dcx()
93+
.struct_span_err(self.token.span, format!("expected item, found {token_str}"));
94+
95+
let stmt = match self.parse_full_stmt(AttemptLocalParseRecovery::No) {
96+
Ok(stmt) => stmt,
97+
Err(e) => {
98+
// We don't really care about an error parsing this statement.
99+
e.cancel();
100+
return Err(err);
101+
}
102+
};
103+
104+
if let Some(stmt) = stmt {
105+
let span = stmt.span;
106+
match &stmt.kind {
107+
StmtKind::Let(_) => {
108+
err.span_label(span, "unexpected `let` binding outside of a function")
109+
.help(format!("consider using `const` or `static` instead of `let` for global variables, or put it inside of a function: fn foo() {{ {} }}",
110+
pprust::stmt_to_string(&stmt)));
111+
}
112+
StmtKind::Semi(expr) | StmtKind::Expr(expr) => {
113+
match expr.kind {
114+
ExprKind::Err(_) => {
115+
err.span_label(expr.span, "error parsing this expression");
116+
}
117+
ExprKind::Array(_) => {
118+
// If it's an array it's possible that it's an attribute
119+
// that is missing a `#`.
120+
err.span_label(span, "unexpected array")
121+
.help(
122+
format!("If you wanted an array consider putting it inside a function: fn foo() {{ {} }}",
123+
pprust::expr_to_string(expr)))
124+
.span_help(span.shrink_to_lo(), "If you wanted an attribute consider adding `#`");
125+
}
126+
ExprKind::Unary(UnOp::Not, _) => {
127+
// This might be an inner attribute wihtout #...
128+
err.span_label(span, "unexpected expression");
129+
}
130+
131+
_ => {
132+
err.span_label(span, "unexpected expression").help(format!(
133+
"consider putting it inside a function: fn foo() {{ {} }}",
134+
pprust::expr_to_string(expr)
135+
));
136+
}
137+
};
138+
}
139+
StmtKind::MacCall(_) => {
140+
err.span_label(span, "unexpected macro call at this position");
141+
}
142+
StmtKind::Item(_) => {
143+
err.span_label(span, "unexpected item at this position");
144+
}
145+
StmtKind::Empty => {
146+
unreachable!("should have been handled by maybe_consume_incorrect_semicolon");
147+
}
148+
};
149+
}
150+
151+
err.note("for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>");
152+
153+
return Err(err);
154+
}
97155
}
98156

99157
pub(super) type ItemInfo = (Ident, ItemKind);
@@ -2289,10 +2347,18 @@ impl<'a> Parser<'a> {
22892347
vis: &Visibility,
22902348
case: Case,
22912349
) -> PResult<'a, (Ident, FnSig, Generics, Option<P<Block>>)> {
2350+
// Let's have an example function
2351+
// const unsafe fn <'a, T> foo(arg: &'a T) -> i32
2352+
// where T: Send {
2353+
// 1+1
2354+
// }
2355+
22922356
let fn_span = self.token.span;
2293-
let header = self.parse_fn_front_matter(vis, case)?; // `const ... fn`
2357+
let header = self.parse_fn_front_matter(vis, case)?; // `const unsafe`
22942358
let ident = self.parse_ident()?; // `foo`
2295-
let mut generics = self.parse_generics()?; // `<'a, T, ...>`
2359+
let mut generics = self.parse_generics()?; // `<'a, T>`
2360+
2361+
// (arg: &'a T) -> i32
22962362
let decl = match self.parse_fn_decl(
22972363
fn_parse_mode.req_name,
22982364
AllowPlus::Yes,
@@ -2309,10 +2375,10 @@ impl<'a> Parser<'a> {
23092375
}
23102376
}
23112377
};
2312-
generics.where_clause = self.parse_where_clause()?; // `where T: Ord`
2378+
generics.where_clause = self.parse_where_clause()?; // `where T: Send`
23132379

23142380
let mut sig_hi = self.prev_token.span;
2315-
let body = self.parse_fn_body(attrs, &ident, &mut sig_hi, fn_parse_mode.req_body)?; // `;` or `{ ... }`.
2381+
let body = self.parse_fn_body(attrs, &ident, &mut sig_hi, fn_parse_mode.req_body)?; // `{ 1+1 }` but could also be `;`
23162382
let fn_sig_span = sig_lo.to(sig_hi);
23172383
Ok((ident, FnSig { header, decl, span: fn_sig_span }, generics, body))
23182384
}

tests/ui/fn/keyword-order.stderr

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ error: expected item, found keyword `pub`
1010
--> $DIR/keyword-order.rs:3:9
1111
|
1212
LL | default pub const async unsafe extern fn err() {}
13-
| ^^^ expected item
13+
| ^^^--------------------------------------
14+
| |
15+
| unexpected item at this position
1416
|
1517
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
1618

tests/ui/impl-trait/precise-capturing/unexpected-token.stderr

+1-3
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@ error: expected item, found `>`
88
--> $DIR/unexpected-token.rs:5:29
99
|
1010
LL | fn hello() -> impl use<'a {}> Sized {}
11-
| ^ expected item
12-
|
13-
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
11+
| ^
1412

1513
error: aborting due to 2 previous errors
1614

tests/ui/parser/default-unmatched.stderr

+1-3
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ error: expected item, found reserved keyword `do`
1010
--> $DIR/default-unmatched.rs:3:13
1111
|
1212
LL | default do
13-
| ^^ expected item
14-
|
15-
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
13+
| ^^
1614

1715
error: aborting due to 2 previous errors
1816

tests/ui/parser/help-set-edition-ice-122130.stderr

+1-3
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@ error: expected item, found `"owned_box"`
1313
--> $DIR/help-set-edition-ice-122130.rs:2:9
1414
|
1515
LL | s#[c"owned_box"]
16-
| ^^^^^^^^^^^ expected item
17-
|
18-
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
16+
| ^^^^^^^^^^^
1917

2018
error: aborting due to 2 previous errors
2119

tests/ui/parser/impl-parsing.stderr

+1-3
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,7 @@ error: expected item, found keyword `unsafe`
3434
--> $DIR/impl-parsing.rs:9:9
3535
|
3636
LL | default unsafe FAIL
37-
| ^^^^^^ expected item
38-
|
39-
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
37+
| ^^^^^^
4038

4139
error: aborting due to 6 previous errors
4240

tests/ui/parser/issues/issue-101477-enum.stderr

+1-3
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ error: expected item, found `==`
1010
--> $DIR/issue-101477-enum.rs:6:7
1111
|
1212
LL | B == 2
13-
| ^^ expected item
14-
|
15-
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
13+
| ^^
1614

1715
error: aborting due to 2 previous errors
1816

tests/ui/parser/issues/issue-113110-non-item-at-module-root.stderr

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ error: expected item, found `5`
22
--> $DIR/issue-113110-non-item-at-module-root.rs:1:2
33
|
44
LL | 5
5-
| ^ expected item
5+
| ^ unexpected expression
66
|
7+
= help: consider putting it inside a function: fn foo() { 5 }
78
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
89

910
error: aborting due to 1 previous error

tests/ui/parser/issues/issue-17904-2.stderr

+1-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ error: expected item, found keyword `where`
22
--> $DIR/issue-17904-2.rs:1:24
33
|
44
LL | struct Bar<T> { x: T } where T: Copy
5-
| ^^^^^ expected item
6-
|
7-
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
5+
| ^^^^^
86

97
error: aborting due to 1 previous error
108

tests/ui/parser/issues/issue-43196.stderr

+1-3
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ error: expected item, found `|`
1010
--> $DIR/issue-43196.rs:5:1
1111
|
1212
LL | |
13-
| ^ expected item
14-
|
15-
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
13+
| ^
1614

1715
error: aborting due to 2 previous errors
1816

tests/ui/parser/issues/issue-62913.stderr

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ error: expected item, found `"\u\"`
1616
--> $DIR/issue-62913.rs:1:1
1717
|
1818
LL | "\u\"
19-
| ^^^^^^ expected item
19+
| ^^^^^^ unexpected expression
2020
|
21+
= help: consider putting it inside a function: fn foo() { "\u\" }
2122
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
2223

2324
error: aborting due to 3 previous errors

tests/ui/parser/issues/issue-68890.stderr

+1-3
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@ error: expected item, found `)`
1818
--> $DIR/issue-68890.rs:1:21
1919
|
2020
LL | enum e{A((?'a a+?+l))}
21-
| ^ expected item
22-
|
23-
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
21+
| ^
2422

2523
error: aborting due to 3 previous errors
2624

tests/ui/parser/shebang/shebang-doc-comment.stderr

+9-1
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,16 @@ error: expected item, found `[`
22
--> $DIR/shebang-doc-comment.rs:2:1
33
|
44
LL | [allow(unused_variables)]
5-
| ^ expected item
5+
| ^------------------------
6+
| |
7+
| unexpected array
68
|
9+
= help: If you wanted an array consider putting it inside a function: fn foo() { [allow(unused_variables)] }
10+
help: If you wanted an attribute consider adding `#`
11+
--> $DIR/shebang-doc-comment.rs:2:1
12+
|
13+
LL | [allow(unused_variables)]
14+
| ^
715
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
816

917
error: aborting due to 1 previous error

tests/ui/parser/suggest-const-for-global-var.stderr

+6-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ error: expected item, found keyword `let`
22
--> $DIR/suggest-const-for-global-var.rs:1:1
33
|
44
LL | let X: i32 = 12;
5-
| ^^^ consider using `const` or `static` instead of `let` for global variables
5+
| ^^^-------------
6+
| |
7+
| unexpected `let` binding outside of a function
8+
|
9+
= help: consider using `const` or `static` instead of `let` for global variables, or put it inside of a function: fn foo() { let X: i32 = 12; }
10+
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
611

712
error: aborting due to 1 previous error
813

tests/ui/parser/virtual-structs.stderr

+1-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ error: expected item, found reserved keyword `virtual`
22
--> $DIR/virtual-structs.rs:3:1
33
|
44
LL | virtual struct SuperStruct {
5-
| ^^^^^^^ expected item
6-
|
7-
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
5+
| ^^^^^^^
86

97
error: aborting due to 1 previous error
108

tests/ui/pub/pub-restricted-error-fn.stderr

+1-3
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ error: expected item, found `(`
1010
--> $DIR/pub-restricted-error-fn.rs:1:12
1111
|
1212
LL | pub(crate) () fn foo() {}
13-
| ^ expected item
14-
|
15-
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
13+
| ^
1614

1715
error: aborting due to 2 previous errors
1816

0 commit comments

Comments
 (0)