@@ -17,7 +17,7 @@ use rustc_ast::{
17
17
} ;
18
18
use rustc_ast_pretty:: pprust;
19
19
use rustc_data_structures:: fx:: FxHashSet ;
20
- use rustc_errors:: { pluralize, struct_span_err, Diagnostic , ErrorGuaranteed } ;
20
+ use rustc_errors:: { pluralize, struct_span_err, Diagnostic , EmissionGuarantee , ErrorGuaranteed } ;
21
21
use rustc_errors:: { Applicability , DiagnosticBuilder , Handler , PResult } ;
22
22
use rustc_span:: source_map:: Spanned ;
23
23
use rustc_span:: symbol:: { kw, Ident } ;
@@ -156,6 +156,89 @@ impl AttemptLocalParseRecovery {
156
156
}
157
157
}
158
158
159
+ /// Information for emitting suggestions and recovering from
160
+ /// C-style `i++`, `--i`, etc.
161
+ #[ derive( Debug , Copy , Clone ) ]
162
+ struct IncDecRecovery {
163
+ /// Is this increment/decrement its own statement?
164
+ standalone : IsStandalone ,
165
+ /// Is this an increment or decrement?
166
+ op : IncOrDec ,
167
+ /// Is this pre- or postfix?
168
+ fixity : UnaryFixity ,
169
+ }
170
+
171
+ /// Is an increment or decrement expression its own statement?
172
+ #[ derive( Debug , Copy , Clone ) ]
173
+ enum IsStandalone {
174
+ /// It's standalone, i.e., its own statement.
175
+ Standalone ,
176
+ /// It's a subexpression, i.e., *not* standalone.
177
+ Subexpr ,
178
+ /// It's maybe standalone; we're not sure.
179
+ Maybe ,
180
+ }
181
+
182
+ #[ derive( Debug , Copy , Clone , PartialEq , Eq ) ]
183
+ enum IncOrDec {
184
+ Inc ,
185
+ // FIXME: `i--` recovery isn't implemented yet
186
+ #[ allow( dead_code) ]
187
+ Dec ,
188
+ }
189
+
190
+ #[ derive( Debug , Copy , Clone , PartialEq , Eq ) ]
191
+ enum UnaryFixity {
192
+ Pre ,
193
+ Post ,
194
+ }
195
+
196
+ impl IncOrDec {
197
+ fn chr ( & self ) -> char {
198
+ match self {
199
+ Self :: Inc => '+' ,
200
+ Self :: Dec => '-' ,
201
+ }
202
+ }
203
+
204
+ fn name ( & self ) -> & ' static str {
205
+ match self {
206
+ Self :: Inc => "increment" ,
207
+ Self :: Dec => "decrement" ,
208
+ }
209
+ }
210
+ }
211
+
212
+ impl std:: fmt:: Display for UnaryFixity {
213
+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
214
+ match self {
215
+ Self :: Pre => write ! ( f, "prefix" ) ,
216
+ Self :: Post => write ! ( f, "postfix" ) ,
217
+ }
218
+ }
219
+ }
220
+
221
+ struct MultiSugg {
222
+ msg : String ,
223
+ patches : Vec < ( Span , String ) > ,
224
+ applicability : Applicability ,
225
+ }
226
+
227
+ impl MultiSugg {
228
+ fn emit < G : EmissionGuarantee > ( self , err : & mut DiagnosticBuilder < ' _ , G > ) {
229
+ err. multipart_suggestion ( & self . msg , self . patches , self . applicability ) ;
230
+ }
231
+
232
+ /// Overrides individual messages and applicabilities.
233
+ fn emit_many < G : EmissionGuarantee > (
234
+ err : & mut DiagnosticBuilder < ' _ , G > ,
235
+ msg : & str ,
236
+ applicability : Applicability ,
237
+ suggestions : impl Iterator < Item = Self > ,
238
+ ) {
239
+ err. multipart_suggestions ( msg, suggestions. map ( |s| s. patches ) , applicability) ;
240
+ }
241
+ }
159
242
// SnapshotParser is used to create a snapshot of the parser
160
243
// without causing duplicate errors being emitted when the `Parser`
161
244
// is dropped.
@@ -1171,6 +1254,135 @@ impl<'a> Parser<'a> {
1171
1254
Ok ( ( ) )
1172
1255
}
1173
1256
1257
+ pub ( super ) fn recover_from_prefix_increment (
1258
+ & mut self ,
1259
+ operand_expr : P < Expr > ,
1260
+ op_span : Span ,
1261
+ prev_is_semi : bool ,
1262
+ ) -> PResult < ' a , P < Expr > > {
1263
+ let standalone =
1264
+ if prev_is_semi { IsStandalone :: Standalone } else { IsStandalone :: Subexpr } ;
1265
+ let kind = IncDecRecovery { standalone, op : IncOrDec :: Inc , fixity : UnaryFixity :: Pre } ;
1266
+
1267
+ self . recover_from_inc_dec ( operand_expr, kind, op_span)
1268
+ }
1269
+
1270
+ pub ( super ) fn recover_from_postfix_increment (
1271
+ & mut self ,
1272
+ operand_expr : P < Expr > ,
1273
+ op_span : Span ,
1274
+ ) -> PResult < ' a , P < Expr > > {
1275
+ let kind = IncDecRecovery {
1276
+ standalone : IsStandalone :: Maybe ,
1277
+ op : IncOrDec :: Inc ,
1278
+ fixity : UnaryFixity :: Post ,
1279
+ } ;
1280
+
1281
+ self . recover_from_inc_dec ( operand_expr, kind, op_span)
1282
+ }
1283
+
1284
+ fn recover_from_inc_dec (
1285
+ & mut self ,
1286
+ base : P < Expr > ,
1287
+ kind : IncDecRecovery ,
1288
+ op_span : Span ,
1289
+ ) -> PResult < ' a , P < Expr > > {
1290
+ let mut err = self . struct_span_err (
1291
+ op_span,
1292
+ & format ! ( "Rust has no {} {} operator" , kind. fixity, kind. op. name( ) ) ,
1293
+ ) ;
1294
+ err. span_label ( op_span, & format ! ( "not a valid {} operator" , kind. fixity) ) ;
1295
+
1296
+ let help_base_case = |mut err : DiagnosticBuilder < ' _ , _ > , base| {
1297
+ err. help ( & format ! ( "use `{}= 1` instead" , kind. op. chr( ) ) ) ;
1298
+ err. emit ( ) ;
1299
+ Ok ( base)
1300
+ } ;
1301
+
1302
+ // (pre, post)
1303
+ let spans = match kind. fixity {
1304
+ UnaryFixity :: Pre => ( op_span, base. span . shrink_to_hi ( ) ) ,
1305
+ UnaryFixity :: Post => ( base. span . shrink_to_lo ( ) , op_span) ,
1306
+ } ;
1307
+
1308
+ match kind. standalone {
1309
+ IsStandalone :: Standalone => self . inc_dec_standalone_suggest ( kind, spans) . emit ( & mut err) ,
1310
+ IsStandalone :: Subexpr => {
1311
+ let Ok ( base_src) = self . span_to_snippet ( base. span )
1312
+ else { return help_base_case ( err, base) } ;
1313
+ match kind. fixity {
1314
+ UnaryFixity :: Pre => {
1315
+ self . prefix_inc_dec_suggest ( base_src, kind, spans) . emit ( & mut err)
1316
+ }
1317
+ UnaryFixity :: Post => {
1318
+ self . postfix_inc_dec_suggest ( base_src, kind, spans) . emit ( & mut err)
1319
+ }
1320
+ }
1321
+ }
1322
+ IsStandalone :: Maybe => {
1323
+ let Ok ( base_src) = self . span_to_snippet ( base. span )
1324
+ else { return help_base_case ( err, base) } ;
1325
+ let sugg1 = match kind. fixity {
1326
+ UnaryFixity :: Pre => self . prefix_inc_dec_suggest ( base_src, kind, spans) ,
1327
+ UnaryFixity :: Post => self . postfix_inc_dec_suggest ( base_src, kind, spans) ,
1328
+ } ;
1329
+ let sugg2 = self . inc_dec_standalone_suggest ( kind, spans) ;
1330
+ MultiSugg :: emit_many (
1331
+ & mut err,
1332
+ "use `+= 1` instead" ,
1333
+ Applicability :: Unspecified ,
1334
+ [ sugg1, sugg2] . into_iter ( ) ,
1335
+ )
1336
+ }
1337
+ }
1338
+ Err ( err)
1339
+ }
1340
+
1341
+ fn prefix_inc_dec_suggest (
1342
+ & mut self ,
1343
+ base_src : String ,
1344
+ kind : IncDecRecovery ,
1345
+ ( pre_span, post_span) : ( Span , Span ) ,
1346
+ ) -> MultiSugg {
1347
+ MultiSugg {
1348
+ msg : format ! ( "use `{}= 1` instead" , kind. op. chr( ) ) ,
1349
+ patches : vec ! [
1350
+ ( pre_span, "{ " . to_string( ) ) ,
1351
+ ( post_span, format!( " {}= 1; {} }}" , kind. op. chr( ) , base_src) ) ,
1352
+ ] ,
1353
+ applicability : Applicability :: MachineApplicable ,
1354
+ }
1355
+ }
1356
+
1357
+ fn postfix_inc_dec_suggest (
1358
+ & mut self ,
1359
+ base_src : String ,
1360
+ kind : IncDecRecovery ,
1361
+ ( pre_span, post_span) : ( Span , Span ) ,
1362
+ ) -> MultiSugg {
1363
+ let tmp_var = if base_src. trim ( ) == "tmp" { "tmp_" } else { "tmp" } ;
1364
+ MultiSugg {
1365
+ msg : format ! ( "use `{}= 1` instead" , kind. op. chr( ) ) ,
1366
+ patches : vec ! [
1367
+ ( pre_span, format!( "{{ let {} = " , tmp_var) ) ,
1368
+ ( post_span, format!( "; {} {}= 1; {} }}" , base_src, kind. op. chr( ) , tmp_var) ) ,
1369
+ ] ,
1370
+ applicability : Applicability :: HasPlaceholders ,
1371
+ }
1372
+ }
1373
+
1374
+ fn inc_dec_standalone_suggest (
1375
+ & mut self ,
1376
+ kind : IncDecRecovery ,
1377
+ ( pre_span, post_span) : ( Span , Span ) ,
1378
+ ) -> MultiSugg {
1379
+ MultiSugg {
1380
+ msg : format ! ( "use `{}= 1` instead" , kind. op. chr( ) ) ,
1381
+ patches : vec ! [ ( pre_span, String :: new( ) ) , ( post_span, format!( " {}= 1" , kind. op. chr( ) ) ) ] ,
1382
+ applicability : Applicability :: MachineApplicable ,
1383
+ }
1384
+ }
1385
+
1174
1386
/// Tries to recover from associated item paths like `[T]::AssocItem` / `(T, U)::AssocItem`.
1175
1387
/// Attempts to convert the base expression/pattern/type into a type, parses the `::AssocItem`
1176
1388
/// tail, and combines them into a `<Ty>::AssocItem` expression/pattern/type.
0 commit comments