@@ -4,12 +4,15 @@ use super::Parser;
4
4
5
5
use crate :: { new_sub_parser_from_file, DirectoryOwnership } ;
6
6
7
- use rustc_errors:: PResult ;
8
- use rustc_span:: source_map:: { FileName , SourceMap , Span , DUMMY_SP } ;
7
+ use rustc_ast_pretty:: pprust;
8
+ use rustc_errors:: { Applicability , PResult } ;
9
+ use rustc_span:: source_map:: { respan, FileName , MultiSpan , SourceMap , Span , DUMMY_SP } ;
9
10
use rustc_span:: symbol:: sym;
10
11
use syntax:: ast:: { self , Attribute , Crate , Ident , ItemKind , Mod } ;
11
12
use syntax:: attr;
13
+ use syntax:: ptr:: P ;
12
14
use syntax:: token:: { self , TokenKind } ;
15
+ use syntax:: visit:: Visitor ;
13
16
14
17
use std:: path:: { self , Path , PathBuf } ;
15
18
@@ -75,9 +78,12 @@ impl<'a> Parser<'a> {
75
78
/// Given a termination token, parses all of the items in a module.
76
79
fn parse_mod_items ( & mut self , term : & TokenKind , inner_lo : Span ) -> PResult < ' a , Mod > {
77
80
let mut items = vec ! [ ] ;
78
- while let Some ( item) = self . parse_item ( ) ? {
79
- items. push ( item) ;
80
- self . maybe_consume_incorrect_semicolon ( & items) ;
81
+ let mut stuck = false ;
82
+ while let Some ( res) = self . parse_item_in_mod ( term, & mut stuck) ? {
83
+ if let Some ( item) = res {
84
+ items. push ( item) ;
85
+ self . maybe_consume_incorrect_semicolon ( & items) ;
86
+ }
81
87
}
82
88
83
89
if !self . eat ( term) {
@@ -95,6 +101,138 @@ impl<'a> Parser<'a> {
95
101
Ok ( Mod { inner : inner_lo. to ( hi) , items, inline : true } )
96
102
}
97
103
104
+ fn parse_item_in_mod (
105
+ & mut self ,
106
+ term : & TokenKind ,
107
+ stuck : & mut bool ,
108
+ ) -> PResult < ' a , Option < Option < P < ast:: Item > > > > {
109
+ match self . parse_item ( ) ? {
110
+ // We just made progress and we might have statements following this item.
111
+ i @ Some ( _) => {
112
+ * stuck = false ;
113
+ Ok ( Some ( i) )
114
+ }
115
+ // No progress and the previous attempt at statements failed, so terminate the loop.
116
+ None if * stuck => Ok ( None ) ,
117
+ None => Ok ( self . recover_stmts_as_item ( term, stuck) ?. then_some ( None ) ) ,
118
+ }
119
+ }
120
+
121
+ /// Parse a contiguous list of statements until we reach the terminating token or EOF.
122
+ /// When any statements were parsed, perform recovery and suggest wrapping the statements
123
+ /// inside a function. If `stuck` becomes `true`, then this method should not be called
124
+ /// unless we have advanced the cursor.
125
+ fn recover_stmts_as_item ( & mut self , term : & TokenKind , stuck : & mut bool ) -> PResult < ' a , bool > {
126
+ let lo = self . token . span ;
127
+ let mut stmts = vec ! [ ] ;
128
+ while ![ term, & token:: Eof ] . contains ( & & self . token . kind ) {
129
+ let old_expected = std:: mem:: take ( & mut self . expected_tokens ) ;
130
+ let snapshot = self . clone ( ) ;
131
+ let stmt = self . parse_full_stmt ( true ) ;
132
+ self . expected_tokens = old_expected; // Restore expected tokens to before recovery.
133
+ match stmt {
134
+ Ok ( None ) => break ,
135
+ Ok ( Some ( stmt) ) => stmts. push ( stmt) ,
136
+ Err ( mut err) => {
137
+ // We couldn't parse as a statement. Rewind to the last one we could for.
138
+ // Also notify the caller that we made no progress, meaning that the method
139
+ // should not be called again to avoid non-termination.
140
+ err. cancel ( ) ;
141
+ * self = snapshot;
142
+ * stuck = true ;
143
+ break ;
144
+ }
145
+ }
146
+ }
147
+
148
+ let recovered = !stmts. is_empty ( ) ;
149
+ if recovered {
150
+ // We parsed some statements and have recovered, so let's emit an error.
151
+ self . error_stmts_as_item_suggest_fn ( lo, stmts) ;
152
+ }
153
+ Ok ( recovered)
154
+ }
155
+
156
+ fn error_stmts_as_item_suggest_fn ( & self , lo : Span , stmts : Vec < ast:: Stmt > ) {
157
+ use syntax:: ast:: * ;
158
+
159
+ let span = lo. to ( self . prev_span ) ;
160
+ let spans: MultiSpan = match & * stmts {
161
+ [ ] | [ _] => span. into ( ) ,
162
+ [ x, .., y] => vec ! [ x. span, y. span] . into ( ) ,
163
+ } ;
164
+
165
+ // Perform coarse grained inference about returns.
166
+ // We use this to tell whether `main` is an acceptable name
167
+ // and if `-> _` or `-> Result<_, _>` should be used instead of defaulting to unit.
168
+ #[ derive( Default ) ]
169
+ struct RetInfer ( bool , bool , bool ) ;
170
+ let RetInfer ( has_ret_unit, has_ret_expr, has_try_expr) = {
171
+ impl Visitor < ' _ > for RetInfer {
172
+ fn visit_expr_post ( & mut self , expr : & Expr ) {
173
+ match expr. kind {
174
+ ExprKind :: Ret ( None ) => self . 0 = true , // `return`
175
+ ExprKind :: Ret ( Some ( _) ) => self . 1 = true , // `return $expr`
176
+ ExprKind :: Try ( _) => self . 2 = true , // `expr?`
177
+ _ => { }
178
+ }
179
+ }
180
+ }
181
+ let mut visitor = RetInfer :: default ( ) ;
182
+ for stmt in & stmts {
183
+ visitor. visit_stmt ( stmt) ;
184
+ }
185
+ if let StmtKind :: Expr ( _) = & stmts. last ( ) . unwrap ( ) . kind {
186
+ visitor. 1 = true ; // The tail expression.
187
+ }
188
+ visitor
189
+ } ;
190
+
191
+ // For the function name, use `main` if we are in `main.rs`, and `my_function` otherwise.
192
+ let use_main = ( has_ret_unit || has_try_expr)
193
+ && self . directory . path . file_stem ( ) == Some ( std:: ffi:: OsStr :: new ( "main" ) ) ;
194
+ let ident = Ident :: from_str_and_span ( if use_main { "main" } else { "my_function" } , span) ;
195
+
196
+ // Construct the return type; either default, `-> _`, or `-> Result<_, _>`.
197
+ let output = match ( has_ret_unit, has_ret_expr, has_try_expr) {
198
+ // `-> ()`; We either had `return;`, so return type is unit, or nothing was returned.
199
+ ( true , _, _) | ( false , false , false ) => FnRetTy :: Default ( span) ,
200
+ // `-> Result<_, _>`; We had `?` somewhere so `-> Result<_, _>` is a good bet.
201
+ ( _, _, true ) => {
202
+ let arg = GenericArg :: Type ( self . mk_ty ( span, TyKind :: Infer ) ) ;
203
+ let args = [ arg. clone ( ) , arg] . to_vec ( ) ;
204
+ let args = AngleBracketedArgs { span, constraints : vec ! [ ] , args } ;
205
+ let mut path = Path :: from_ident ( Ident :: from_str_and_span ( "Result" , span) ) ;
206
+ path. segments [ 0 ] . args = Some ( P ( GenericArgs :: AngleBracketed ( args) ) ) ;
207
+ FnRetTy :: Ty ( self . mk_ty ( span, TyKind :: Path ( None , path) ) )
208
+ }
209
+ // `-> _`; We had `return $expr;` so it's probably not `()` as return type.
210
+ ( _, true , _) => FnRetTy :: Ty ( self . mk_ty ( span, TyKind :: Infer ) ) ,
211
+ } ;
212
+
213
+ // Finalize the AST for the function item: `fn $ident() $output { $stmts }`.
214
+ let sig = FnSig { header : FnHeader :: default ( ) , decl : P ( FnDecl { inputs : vec ! [ ] , output } ) } ;
215
+ let body = self . mk_block ( stmts, BlockCheckMode :: Default , span) ;
216
+ let kind = ItemKind :: Fn ( Defaultness :: Final , sig, Generics :: default ( ) , Some ( body) ) ;
217
+ let vis = respan ( span, VisibilityKind :: Inherited ) ;
218
+ let item = Item { span, ident, vis, kind, attrs : vec ! [ ] , id : DUMMY_NODE_ID , tokens : None } ;
219
+
220
+ // Emit the error with a suggestion to wrap the statements in the function.
221
+ let mut err = self . struct_span_err ( spans, "statements cannot reside in modules" ) ;
222
+ err. span_suggestion_verbose (
223
+ span,
224
+ "consider moving the statements into a function" ,
225
+ pprust:: item_to_string ( & item) ,
226
+ Applicability :: HasPlaceholders ,
227
+ ) ;
228
+ err. note ( "the program entry point starts in `fn main() { ... }`, defined in `main.rs`" ) ;
229
+ err. note (
230
+ "for more on functions and how to structure your program, \
231
+ see https://doc.rust-lang.org/book/ch03-03-how-functions-work.html",
232
+ ) ;
233
+ err. emit ( ) ;
234
+ }
235
+
98
236
fn submod_path (
99
237
& mut self ,
100
238
id : ast:: Ident ,
0 commit comments