Skip to content

Commit c9cc43a

Browse files
committed
Move increment checks to improve errors
1 parent 5d9cd4b commit c9cc43a

File tree

5 files changed

+286
-79
lines changed

5 files changed

+286
-79
lines changed

compiler/rustc_parse/src/parser/diagnostics.rs

Lines changed: 185 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,52 @@ impl AttemptLocalParseRecovery {
156156
}
157157
}
158158

159+
#[derive(Debug, Copy, Clone)]
160+
struct IncDecRecovery {
161+
standalone: bool,
162+
op: IncOrDec,
163+
fixity: UnaryFixity,
164+
}
165+
166+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
167+
enum IncOrDec {
168+
Inc,
169+
// FIXME: `i--` recovery isn't implemented yet
170+
#[allow(dead_code)]
171+
Dec,
172+
}
173+
174+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
175+
enum UnaryFixity {
176+
Pre,
177+
Post,
178+
}
179+
180+
impl IncOrDec {
181+
fn chr(&self) -> char {
182+
match self {
183+
Self::Inc => '+',
184+
Self::Dec => '-',
185+
}
186+
}
187+
188+
fn name(&self) -> &'static str {
189+
match self {
190+
Self::Inc => "increment",
191+
Self::Dec => "decrement",
192+
}
193+
}
194+
}
195+
196+
impl std::fmt::Display for UnaryFixity {
197+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
198+
match self {
199+
Self::Pre => write!(f, "prefix"),
200+
Self::Post => write!(f, "postfix"),
201+
}
202+
}
203+
}
204+
159205
// SnapshotParser is used to create a snapshot of the parser
160206
// without causing duplicate errors being emitted when the `Parser`
161207
// is dropped.
@@ -1167,6 +1213,145 @@ impl<'a> Parser<'a> {
11671213
Ok(())
11681214
}
11691215

1216+
pub(super) fn maybe_recover_from_prefix_increment(
1217+
&mut self,
1218+
operand_expr: P<Expr>,
1219+
op_span: Span,
1220+
prev_is_semi: bool,
1221+
) -> PResult<'a, P<Expr>> {
1222+
let kind = IncDecRecovery {
1223+
standalone: prev_is_semi,
1224+
op: IncOrDec::Inc,
1225+
fixity: UnaryFixity::Pre,
1226+
};
1227+
1228+
self.recover_from_inc_dec(operand_expr, kind, op_span)
1229+
}
1230+
1231+
pub(super) fn maybe_recover_from_postfix_increment(
1232+
&mut self,
1233+
operand_expr: P<Expr>,
1234+
op_span: Span,
1235+
prev_is_semi: bool,
1236+
) -> PResult<'a, P<Expr>> {
1237+
let kind = IncDecRecovery {
1238+
standalone: prev_is_semi,
1239+
op: IncOrDec::Inc,
1240+
fixity: UnaryFixity::Post,
1241+
};
1242+
1243+
self.recover_from_inc_dec(operand_expr, kind, op_span)
1244+
}
1245+
1246+
fn recover_from_inc_dec(
1247+
&mut self,
1248+
base: P<Expr>,
1249+
kind: IncDecRecovery,
1250+
op_span: Span,
1251+
) -> PResult<'a, P<Expr>> {
1252+
let mut err = self.struct_span_err(
1253+
op_span,
1254+
&format!("Rust has no {} {} operator", kind.fixity, kind.op.name()),
1255+
);
1256+
err.span_label(op_span, &format!("not a valid {} operator", kind.fixity));
1257+
1258+
if let ExprKind::Path(_, path) = &base.kind {
1259+
if let [segment] = path.segments.as_slice() {
1260+
let ident = segment.ident;
1261+
// (pre, post)
1262+
let spans = match kind.fixity {
1263+
UnaryFixity::Pre => (op_span, ident.span.shrink_to_hi()),
1264+
UnaryFixity::Post => (ident.span.shrink_to_lo(), op_span),
1265+
};
1266+
1267+
if !ident.is_reserved() {
1268+
if kind.standalone {
1269+
return self.inc_dec_standalone_recovery(base, err, kind, ident, spans);
1270+
} else {
1271+
match kind.fixity {
1272+
UnaryFixity::Pre => {
1273+
return self.prefix_inc_dec_suggest_and_recover(
1274+
base, err, kind, ident, spans,
1275+
);
1276+
}
1277+
UnaryFixity::Post => {
1278+
return self.postfix_inc_dec_suggest_and_recover(
1279+
base, err, kind, ident, spans,
1280+
);
1281+
}
1282+
}
1283+
}
1284+
}
1285+
}
1286+
}
1287+
1288+
// base case
1289+
err.help(&format!("use `{}= 1` instead", kind.op.chr()));
1290+
err.emit();
1291+
1292+
Ok(base)
1293+
}
1294+
1295+
fn prefix_inc_dec_suggest_and_recover(
1296+
&mut self,
1297+
base: P<Expr>,
1298+
mut err: DiagnosticBuilder<'_>,
1299+
kind: IncDecRecovery,
1300+
ident: Ident,
1301+
(pre_span, post_span): (Span, Span),
1302+
) -> PResult<'a, P<Expr>> {
1303+
err.multipart_suggestion(
1304+
&format!("use `{}= 1` instead", kind.op.chr()),
1305+
vec![
1306+
(pre_span, "{ ".to_string()),
1307+
(post_span, format!(" {}= 1; {} }}", kind.op.chr(), ident)),
1308+
],
1309+
Applicability::MachineApplicable,
1310+
);
1311+
err.emit();
1312+
// TODO: recover
1313+
Ok(base)
1314+
}
1315+
1316+
fn postfix_inc_dec_suggest_and_recover(
1317+
&mut self,
1318+
base: P<Expr>,
1319+
mut err: DiagnosticBuilder<'_>,
1320+
kind: IncDecRecovery,
1321+
ident: Ident,
1322+
(pre_span, post_span): (Span, Span),
1323+
) -> PResult<'a, P<Expr>> {
1324+
err.multipart_suggestion(
1325+
&format!("use `{}= 1` instead", kind.op.chr()),
1326+
vec![
1327+
(pre_span, "{ let tmp = ".to_string()),
1328+
(post_span, format!("; {} {}= 1; tmp }}", ident, kind.op.chr())),
1329+
],
1330+
Applicability::MachineApplicable,
1331+
);
1332+
err.emit();
1333+
// TODO: recover
1334+
Ok(base)
1335+
}
1336+
1337+
fn inc_dec_standalone_recovery(
1338+
&mut self,
1339+
base: P<Expr>,
1340+
mut err: DiagnosticBuilder<'_>,
1341+
kind: IncDecRecovery,
1342+
_ident: Ident,
1343+
(pre_span, post_span): (Span, Span),
1344+
) -> PResult<'a, P<Expr>> {
1345+
err.multipart_suggestion(
1346+
&format!("use `{}= 1` instead", kind.op.chr()),
1347+
vec![(pre_span, String::new()), (post_span, format!(" {}= 1", kind.op.chr()))],
1348+
Applicability::MachineApplicable,
1349+
);
1350+
err.emit();
1351+
// TODO: recover
1352+
Ok(base)
1353+
}
1354+
11701355
/// Tries to recover from associated item paths like `[T]::AssocItem` / `(T, U)::AssocItem`.
11711356
/// Attempts to convert the base expression/pattern/type into a type, parses the `::AssocItem`
11721357
/// tail, and combines them into a `<Ty>::AssocItem` expression/pattern/type.
@@ -1882,49 +2067,6 @@ impl<'a> Parser<'a> {
18822067
self.sess.expr_parentheses_needed(&mut err, *sp);
18832068
}
18842069
err.span_label(span, "expected expression");
1885-
if self.prev_token.kind == TokenKind::BinOp(token::Plus)
1886-
&& self.token.kind == TokenKind::BinOp(token::Plus)
1887-
&& self.look_ahead(1, |t| !t.is_lit())
1888-
{
1889-
let span = self.prev_token.span.to(self.token.span);
1890-
err.note("Rust has no dedicated increment operator");
1891-
err.span_suggestion_verbose(
1892-
span,
1893-
"try using `+= 1` instead",
1894-
" += 1".into(),
1895-
Applicability::Unspecified,
1896-
);
1897-
} else if self.token.kind == TokenKind::BinOp(token::Plus)
1898-
&& self.look_ahead(1, |t| t.kind == TokenKind::BinOp(token::Plus))
1899-
&& self.look_ahead(2, |t| !t.is_lit())
1900-
{
1901-
let target_span = self.look_ahead(2, |t| t.span);
1902-
let left_brace_span = target_span.shrink_to_lo();
1903-
let pre_span = self.token.span.to(self.look_ahead(1, |t| t.span));
1904-
let post_span = target_span.shrink_to_hi();
1905-
1906-
err.note("Rust has no dedicated increment operator");
1907-
1908-
if self.prev_token.kind == TokenKind::Semi {
1909-
err.multipart_suggestion(
1910-
"try using `+= 1` instead",
1911-
vec![(pre_span, String::new()), (post_span, " += 1".into())],
1912-
Applicability::MachineApplicable,
1913-
);
1914-
} else if let Ok(target_snippet) = self.span_to_snippet(target_span) {
1915-
err.multipart_suggestion(
1916-
"try using `+= 1` instead",
1917-
vec![
1918-
(left_brace_span, "{ ".to_string()),
1919-
(pre_span, String::new()),
1920-
(post_span, format!(" += 1; {} }}", target_snippet)),
1921-
],
1922-
Applicability::MachineApplicable,
1923-
);
1924-
} else {
1925-
err.span_help(pre_span.to(target_span), "try using `+= 1` instead");
1926-
}
1927-
}
19282070
err
19292071
}
19302072

compiler/rustc_parse/src/parser/expr.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,18 @@ impl<'a> Parser<'a> {
267267
self.bump();
268268
}
269269

270+
if self.prev_token == token::BinOp(token::Plus)
271+
&& self.token == token::BinOp(token::Plus)
272+
{
273+
let op_span = self.prev_token.span.to(self.token.span);
274+
// Eat the second `+`
275+
self.bump();
276+
// TODO: implement
277+
let start_is_semi = false;
278+
lhs = self.maybe_recover_from_postfix_increment(lhs, op_span, start_is_semi)?;
279+
continue;
280+
}
281+
270282
let op = op.node;
271283
// Special cases:
272284
if op == AssocOp::As {
@@ -586,6 +598,19 @@ impl<'a> Parser<'a> {
586598
token::Ident(..) if this.is_mistaken_not_ident_negation() => {
587599
make_it!(this, attrs, |this, _| this.recover_not_expr(lo))
588600
}
601+
// Recover from `++x`
602+
token::BinOp(token::Plus)
603+
if this.look_ahead(1, |t| *t == token::BinOp(token::Plus)) =>
604+
{
605+
let prev_is_semi = this.prev_token == token::Semi;
606+
let pre_span = this.token.span.to(this.look_ahead(1, |t| t.span));
607+
// Eat both `+`s.
608+
this.bump();
609+
this.bump();
610+
611+
let operand_expr = this.parse_path_start_expr(Default::default())?;
612+
this.maybe_recover_from_prefix_increment(operand_expr, pre_span, prev_is_semi)
613+
}
589614
_ => return this.parse_dot_or_call_expr(Some(attrs)),
590615
}
591616
}

src/test/ui/parser/increment.fixed

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// run-rustfix
2+
3+
fn post_regular() {
4+
let mut i = 0;
5+
{ let tmp = i; i += 1; tmp }; //~ ERROR Rust has no postfix increment operator
6+
println!("{}", i);
7+
}
8+
9+
fn post_while() {
10+
let mut i = 0;
11+
while { let tmp = i; i += 1; tmp } < 5 {
12+
//~^ ERROR Rust has no postfix increment operator
13+
println!("{}", i);
14+
}
15+
}
16+
17+
fn pre_regular() {
18+
let mut i = 0;
19+
i += 1; //~ ERROR Rust has no prefix increment operator
20+
println!("{}", i);
21+
}
22+
23+
fn pre_while() {
24+
let mut i = 0;
25+
while { i += 1; i } < 5 {
26+
//~^ ERROR Rust has no prefix increment operator
27+
println!("{}", i);
28+
}
29+
}
30+
31+
fn main() {
32+
post_regular();
33+
post_while();
34+
pre_regular();
35+
pre_while();
36+
}

src/test/ui/parser/increment.rs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,36 @@
1+
// run-rustfix
2+
13
fn post_regular() {
2-
let i = 0;
3-
i++; //~ ERROR
4+
let mut i = 0;
5+
i++; //~ ERROR Rust has no postfix increment operator
6+
println!("{}", i);
47
}
58

69
fn post_while() {
7-
let i = 0;
10+
let mut i = 0;
811
while i++ < 5 {
9-
//~^ ERROR
12+
//~^ ERROR Rust has no postfix increment operator
1013
println!("{}", i);
1114
}
1215
}
1316

1417
fn pre_regular() {
15-
let i = 0;
16-
++i; //~ ERROR
18+
let mut i = 0;
19+
++i; //~ ERROR Rust has no prefix increment operator
20+
println!("{}", i);
1721
}
1822

1923
fn pre_while() {
20-
let i = 0;
24+
let mut i = 0;
2125
while ++i < 5 {
22-
//~^ ERROR
26+
//~^ ERROR Rust has no prefix increment operator
2327
println!("{}", i);
2428
}
2529
}
2630

27-
fn main() {}
31+
fn main() {
32+
post_regular();
33+
post_while();
34+
pre_regular();
35+
pre_while();
36+
}

0 commit comments

Comments
 (0)