Skip to content

Commit

Permalink
improve parse item fallback
Browse files Browse the repository at this point in the history
  • Loading branch information
dev-ardi authored and ardi committed Jun 20, 2024
1 parent 4333551 commit b9ddf53
Show file tree
Hide file tree
Showing 17 changed files with 127 additions and 51 deletions.
8 changes: 8 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ pub fn attribute_to_string(attr: &ast::Attribute) -> String {
State::new().attribute_to_string(attr)
}

pub fn local_to_string(local: &ast::Local) -> String {
State::new().local_to_string(local)
}

pub fn stmt_to_string(stmt: &ast::Stmt) -> String {
State::new().stmt_to_string(stmt)
}

pub fn to_string(f: impl FnOnce(&mut State<'_>)) -> String {
State::to_string(f)
}
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2076,4 +2076,8 @@ impl<'a> State<'a> {
pub(crate) fn attribute_to_string(&self, attr: &ast::Attribute) -> String {
Self::to_string(|s| s.print_attribute(attr))
}

pub(crate) fn local_to_string(&self, local: &ast::Local) -> String {
Self::to_string(|s| s.print_local_decl(local))
}
}
103 changes: 84 additions & 19 deletions compiler/rustc_parse/src/parser/item.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use super::diagnostics::{dummy_arg, ConsumeClosingDelim};
use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
use super::{
AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle, Trailing, TrailingToken,
AttemptLocalParseRecovery, AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle,
Trailing, TrailingToken,
};
use crate::errors::{self, MacroExpandsToAdtField};
use crate::fluent_generated as fluent;
Expand Down Expand Up @@ -40,6 +41,7 @@ impl<'a> Parser<'a> {
let mod_kind = if self.eat(&token::Semi) {
ModKind::Unloaded
} else {
// mod blocks are parsed recursively
self.expect(&token::OpenDelim(Delimiter::Brace))?;
let (inner_attrs, items, inner_span) =
self.parse_mod(&token::CloseDelim(Delimiter::Brace))?;
Expand Down Expand Up @@ -72,28 +74,83 @@ impl<'a> Parser<'a> {
}

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

let inject_use_span = post_attr_lo.data().with_hi(post_attr_lo.lo());
let mod_spans = ModSpans { inner_span: lo.to(self.prev_token.span), inject_use_span };
Ok((attrs, items, mod_spans))
}

fn fallback_incorrect_item(&mut self) -> PResult<'a, ()> {
let token_str = super::token_descr(&self.token);
let mut err = self
.dcx()
.struct_span_err(self.token.span, format!("expected item, found {token_str}"));

let stmt = match self.parse_full_stmt(AttemptLocalParseRecovery::No) {
Ok(stmt) => stmt,
Err(e) => {
// We don't really care about an error parsing this statement.
e.cancel();
return Err(err);
}
};

if let Some(stmt) = stmt {
let span = stmt.span;
match &stmt.kind {
StmtKind::Let(_) => {
err.span_label(span, "unexpected `let` binding outside of a function")
.help(format!("consider using `const` or `static` instead of `let` for global variables, or put it inside of a function: fn foo() {{ {} }}",
pprust::stmt_to_string(&stmt)));
}
StmtKind::Semi(expr) | StmtKind::Expr(expr) => {
match expr.kind {
ExprKind::Err(_) => {
err.span_label(expr.span, "error parsing this expression");
}
ExprKind::Array(_) => {
// If it's an array it's possible that it's an attribute
// that is missing a `#`.
err.span_label(span, "unexpected array")
.help(
format!("If you wanted an array consider putting it inside a function: fn foo() {{ {} }}",
pprust::expr_to_string(expr)))
.span_help(span.shrink_to_lo(), "If you wanted an attribute consider adding `#`");
}
ExprKind::Unary(UnOp::Not, _) => {
// This might be an inner attribute wihtout #...
err.span_label(span, "unexpected expression");
}
_ => {
err.span_label(span, "unexpected expression").help(format!(
"consider putting it inside a function: fn foo() {{ {} }}",
pprust::expr_to_string(expr)
));
}
};
}
StmtKind::MacCall(_) => {
err.span_label(span, "unexpected macro call at this position");
}
StmtKind::Item(_) => {
err.span_label(span, "unexpected item at this position");
}
StmtKind::Empty => {
unreachable!("should have been handled by maybe_consume_incorrect_semicolon");
}
};
}

err.note("for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>");

return Err(err);
}
}

pub(super) type ItemInfo = (Ident, ItemKind);
Expand Down Expand Up @@ -2312,10 +2369,18 @@ impl<'a> Parser<'a> {
vis: &Visibility,
case: Case,
) -> PResult<'a, (Ident, FnSig, Generics, Option<P<Block>>)> {
// Let's have an example function
// const unsafe fn <'a, T> foo(arg: &'a T) -> i32
// where T: Send {
// 1+1
// }

let fn_span = self.token.span;
let header = self.parse_fn_front_matter(vis, case)?; // `const ... fn`
let header = self.parse_fn_front_matter(vis, case)?; // `const unsafe`
let ident = self.parse_ident()?; // `foo`
let mut generics = self.parse_generics()?; // `<'a, T, ...>`
let mut generics = self.parse_generics()?; // `<'a, T>`

// (arg: &'a T) -> i32
let decl = match self.parse_fn_decl(
fn_parse_mode.req_name,
AllowPlus::Yes,
Expand All @@ -2332,10 +2397,10 @@ impl<'a> Parser<'a> {
}
}
};
generics.where_clause = self.parse_where_clause()?; // `where T: Ord`
generics.where_clause = self.parse_where_clause()?; // `where T: Send`

let mut sig_hi = self.prev_token.span;
let body = self.parse_fn_body(attrs, &ident, &mut sig_hi, fn_parse_mode.req_body)?; // `;` or `{ ... }`.
let body = self.parse_fn_body(attrs, &ident, &mut sig_hi, fn_parse_mode.req_body)?; // `{ 1+1 }` but could also be `;`
let fn_sig_span = sig_lo.to(sig_hi);
Ok((ident, FnSig { header, decl, span: fn_sig_span }, generics, body))
}
Expand Down
4 changes: 3 additions & 1 deletion tests/ui/fn/keyword-order.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ error: expected item, found keyword `pub`
--> $DIR/keyword-order.rs:3:9
|
LL | default pub const async unsafe extern fn err() {}
| ^^^ expected item
| ^^^--------------------------------------
| |
| unexpected item at this position
|
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>

Expand Down
4 changes: 1 addition & 3 deletions tests/ui/parser/default-unmatched.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ error: expected item, found reserved keyword `do`
--> $DIR/default-unmatched.rs:3:13
|
LL | default do
| ^^ expected item
|
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
| ^^

error: aborting due to 2 previous errors

4 changes: 1 addition & 3 deletions tests/ui/parser/help-set-edition-ice-122130.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ error: expected item, found `"owned_box"`
--> $DIR/help-set-edition-ice-122130.rs:2:9
|
LL | s#[c"owned_box"]
| ^^^^^^^^^^^ expected item
|
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
| ^^^^^^^^^^^

error: aborting due to 2 previous errors

4 changes: 1 addition & 3 deletions tests/ui/parser/impl-parsing.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,7 @@ error: expected item, found keyword `unsafe`
--> $DIR/impl-parsing.rs:9:9
|
LL | default unsafe FAIL
| ^^^^^^ expected item
|
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
| ^^^^^^

error: aborting due to 6 previous errors

4 changes: 1 addition & 3 deletions tests/ui/parser/issues/issue-101477-enum.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ error: expected item, found `==`
--> $DIR/issue-101477-enum.rs:6:7
|
LL | B == 2
| ^^ expected item
|
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
| ^^

error: aborting due to 2 previous errors

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

error: aborting due to 1 previous error
Expand Down
4 changes: 1 addition & 3 deletions tests/ui/parser/issues/issue-17904-2.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ error: expected item, found keyword `where`
--> $DIR/issue-17904-2.rs:1:24
|
LL | struct Bar<T> { x: T } where T: Copy
| ^^^^^ expected item
|
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
| ^^^^^

error: aborting due to 1 previous error

4 changes: 1 addition & 3 deletions tests/ui/parser/issues/issue-43196.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ error: expected item, found `|`
--> $DIR/issue-43196.rs:5:1
|
LL | |
| ^ expected item
|
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
| ^

error: aborting due to 2 previous errors

3 changes: 2 additions & 1 deletion tests/ui/parser/issues/issue-62913.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ error: expected item, found `"\u\"`
--> $DIR/issue-62913.rs:1:1
|
LL | "\u\"
| ^^^^^^ expected item
| ^^^^^^ unexpected expression
|
= help: consider putting it inside a function: fn foo() { "\u\" }
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>

error: aborting due to 3 previous errors
Expand Down
4 changes: 1 addition & 3 deletions tests/ui/parser/issues/issue-68890.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ error: expected item, found `)`
--> $DIR/issue-68890.rs:1:21
|
LL | enum e{A((?'a a+?+l))}
| ^ expected item
|
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
| ^

error: aborting due to 3 previous errors

10 changes: 9 additions & 1 deletion tests/ui/parser/shebang/shebang-doc-comment.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,16 @@ error: expected item, found `[`
--> $DIR/shebang-doc-comment.rs:2:1
|
LL | [allow(unused_variables)]
| ^ expected item
| ^------------------------
| |
| unexpected array
|
= help: If you wanted an array consider putting it inside a function: fn foo() { [allow(unused_variables)] }
help: If you wanted an attribute consider adding `#`
--> $DIR/shebang-doc-comment.rs:2:1
|
LL | [allow(unused_variables)]
| ^
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>

error: aborting due to 1 previous error
Expand Down
7 changes: 6 additions & 1 deletion tests/ui/parser/suggest-const-for-global-var.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ error: expected item, found keyword `let`
--> $DIR/suggest-const-for-global-var.rs:1:1
|
LL | let X: i32 = 12;
| ^^^ consider using `const` or `static` instead of `let` for global variables
| ^^^-------------
| |
| unexpected `let` binding outside of a function
|
= 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; }
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>

error: aborting due to 1 previous error

4 changes: 1 addition & 3 deletions tests/ui/parser/virtual-structs.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ error: expected item, found reserved keyword `virtual`
--> $DIR/virtual-structs.rs:3:1
|
LL | virtual struct SuperStruct {
| ^^^^^^^ expected item
|
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
| ^^^^^^^

error: aborting due to 1 previous error

4 changes: 1 addition & 3 deletions tests/ui/pub/pub-restricted-error-fn.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ error: expected item, found `(`
--> $DIR/pub-restricted-error-fn.rs:1:12
|
LL | pub(crate) () fn foo() {}
| ^ expected item
|
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
| ^

error: aborting due to 2 previous errors

0 comments on commit b9ddf53

Please sign in to comment.