@@ -17,7 +17,7 @@ use rustc_ast::{
1717} ;
1818use rustc_ast_pretty:: pprust;
1919use 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 } ;
2121use rustc_errors:: { Applicability , DiagnosticBuilder , Handler , PResult } ;
2222use rustc_span:: source_map:: Spanned ;
2323use rustc_span:: symbol:: { kw, Ident } ;
@@ -156,6 +156,89 @@ impl AttemptLocalParseRecovery {
156156 }
157157}
158158
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+ }
159242// SnapshotParser is used to create a snapshot of the parser
160243// without causing duplicate errors being emitted when the `Parser`
161244// is dropped.
@@ -1171,6 +1254,135 @@ impl<'a> Parser<'a> {
11711254 Ok ( ( ) )
11721255 }
11731256
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+
11741386 /// Tries to recover from associated item paths like `[T]::AssocItem` / `(T, U)::AssocItem`.
11751387 /// Attempts to convert the base expression/pattern/type into a type, parses the `::AssocItem`
11761388 /// tail, and combines them into a `<Ty>::AssocItem` expression/pattern/type.
0 commit comments