@@ -21,7 +21,8 @@ use tracing::debug;
2121use super :: diagnostics:: { ConsumeClosingDelim , dummy_arg} ;
2222use super :: ty:: { AllowPlus , RecoverQPath , RecoverReturnSign } ;
2323use super :: {
24- AttrWrapper , FollowedByType , ForceCollect , Parser , PathStyle , Trailing , UsePreAttrPos ,
24+ AttemptLocalParseRecovery , AttrWrapper , FollowedByType , ForceCollect , Parser , PathStyle ,
25+ Trailing , UsePreAttrPos ,
2526} ;
2627use crate :: errors:: { self , MacroExpandsToAdtField } ;
2728use crate :: { fluent_generated as fluent, maybe_whole} ;
@@ -74,21 +75,11 @@ impl<'a> Parser<'a> {
7475 items. push ( item) ;
7576 }
7677
78+ // The last token should be `term`: either EOF or `}`. If it's not that means that we've had an error
79+ // parsing an item
7780 if !self . eat ( term) {
78- let token_str = super :: token_descr ( & self . token ) ;
7981 if !self . maybe_consume_incorrect_semicolon ( items. last ( ) . map ( |x| & * * x) ) {
80- let msg = format ! ( "expected item, found {token_str}" ) ;
81- let mut err = self . dcx ( ) . struct_span_err ( self . token . span , msg) ;
82- let span = self . token . span ;
83- if self . is_kw_followed_by_ident ( kw:: Let ) {
84- err. span_label (
85- span,
86- "consider using `const` or `static` instead of `let` for global variables" ,
87- ) ;
88- } else {
89- err. span_label ( span, "expected item" )
90- . note ( "for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>" ) ;
91- } ;
82+ let err = self . fallback_incorrect_item ( ) ;
9283 return Err ( err) ;
9384 }
9485 }
@@ -97,6 +88,64 @@ impl<'a> Parser<'a> {
9788 let mod_spans = ModSpans { inner_span : lo. to ( self . prev_token . span ) , inject_use_span } ;
9889 Ok ( ( attrs, items, mod_spans) )
9990 }
91+
92+ /// Tries to parse the item as a statement to provide further diagnostics.
93+ fn fallback_incorrect_item ( & mut self ) -> rustc_errors:: Diag < ' a > {
94+ let token_str = super :: token_descr ( & self . token ) ;
95+ let token_span = self . token . span ;
96+ let mut err =
97+ self . dcx ( ) . struct_span_err ( token_span, format ! ( "expected item, found {token_str}" ) ) ;
98+
99+ let mut do_default_diag = true ;
100+
101+ match self . parse_full_stmt ( AttemptLocalParseRecovery :: No ) {
102+ Ok ( Some ( stmt) ) => {
103+ do_default_diag = false ;
104+ let span = stmt. span ;
105+ match & stmt. kind {
106+ StmtKind :: Let ( _) => {
107+ err. span_label ( span, "unexpected `let` binding outside of a function" )
108+ . help ( format ! ( "consider using `const` or `static` instead of `let` for global variables, or put it inside of a function: fn foo() {{ {} }}" ,
109+ pprust:: stmt_to_string( & stmt) ) ) ;
110+ }
111+ StmtKind :: Semi ( expr) => {
112+ err. span_label ( span, "unexpected expression" ) . help ( format ! (
113+ "consider putting it inside a function: fn foo() {{ {}; }}" ,
114+ pprust:: expr_to_string( expr)
115+ ) ) ;
116+ }
117+ StmtKind :: Expr ( expr) => {
118+ err. span_label ( span, "unexpected expression" ) . help ( format ! (
119+ "consider putting it inside a function: fn foo() {{ {} }}" ,
120+ pprust:: expr_to_string( expr)
121+ ) ) ;
122+ }
123+ StmtKind :: Empty => {
124+ unreachable ! (
125+ "Should have been handled by maybe_consume_incorrect_semicolon"
126+ ) ;
127+ }
128+ StmtKind :: Item ( _) | StmtKind :: MacCall ( _) => {
129+ unreachable ! ( "These should be valid items!" )
130+ }
131+ } ;
132+ }
133+ // It's not a statement, we can't do much recovery.
134+ Ok ( None ) => { }
135+ Err ( e) => {
136+ // We don't really care about an error parsing this statement.
137+ e. cancel ( ) ;
138+ }
139+ }
140+
141+ if do_default_diag {
142+ err. span_label ( token_span, "expected item" ) ;
143+ }
144+
145+ err. note ( "for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>" ) ;
146+
147+ err
148+ }
100149}
101150
102151pub ( super ) type ItemInfo = ( Ident , ItemKind ) ;
0 commit comments