Skip to content

Commit

Permalink
Fix handling of unmatched angle brackets in parser
Browse files Browse the repository at this point in the history
  • Loading branch information
FabianWolff committed Jun 4, 2021
1 parent 4e219e6 commit 6a6a605
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 37 deletions.
84 changes: 47 additions & 37 deletions compiler/rustc_parse/src/parser/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,49 +352,59 @@ impl<'a> Parser<'a> {
debug!("parse_generic_args_with_leading_angle_bracket_recovery: (snapshotting)");
match self.parse_angle_args() {
Ok(args) => Ok(args),
Err(ref mut e) if is_first_invocation && self.unmatched_angle_bracket_count > 0 => {
// Cancel error from being unable to find `>`. We know the error
// must have been this due to a non-zero unmatched angle bracket
// count.
e.cancel();

Err(mut e) if is_first_invocation && self.unmatched_angle_bracket_count > 0 => {
// Swap `self` with our backup of the parser state before attempting to parse
// generic arguments.
let snapshot = mem::replace(self, snapshot.unwrap());

debug!(
"parse_generic_args_with_leading_angle_bracket_recovery: (snapshot failure) \
snapshot.count={:?}",
snapshot.unmatched_angle_bracket_count,
);

// Eat the unmatched angle brackets.
for _ in 0..snapshot.unmatched_angle_bracket_count {
self.eat_lt();
}

// Make a span over ${unmatched angle bracket count} characters.
let span = lo.with_hi(lo.lo() + BytePos(snapshot.unmatched_angle_bracket_count));
self.struct_span_err(
span,
&format!(
"unmatched angle bracket{}",
pluralize!(snapshot.unmatched_angle_bracket_count)
),
)
.span_suggestion(
span,
&format!(
"remove extra angle bracket{}",
pluralize!(snapshot.unmatched_angle_bracket_count)
),
String::new(),
Applicability::MachineApplicable,
)
.emit();
let all_angle_brackets = (0..snapshot.unmatched_angle_bracket_count)
.fold(true, |a, _| a && self.eat_lt());

if !all_angle_brackets {
// If there are other tokens in between the extraneous `<`s, we cannot simply
// suggest to remove them. This check also prevents us from accidentally ending
// up in the middle of a multibyte character (issue #84104).
let _ = mem::replace(self, snapshot);
Err(e)
} else {
// Cancel error from being unable to find `>`. We know the error
// must have been this due to a non-zero unmatched angle bracket
// count.
e.cancel();

debug!(
"parse_generic_args_with_leading_angle_bracket_recovery: (snapshot failure) \
snapshot.count={:?}",
snapshot.unmatched_angle_bracket_count,
);

// Make a span over ${unmatched angle bracket count} characters.
// This is safe because `all_angle_brackets` ensures that there are only `<`s,
// i.e. no multibyte characters, in this range.
let span =
lo.with_hi(lo.lo() + BytePos(snapshot.unmatched_angle_bracket_count));
self.struct_span_err(
span,
&format!(
"unmatched angle bracket{}",
pluralize!(snapshot.unmatched_angle_bracket_count)
),
)
.span_suggestion(
span,
&format!(
"remove extra angle bracket{}",
pluralize!(snapshot.unmatched_angle_bracket_count)
),
String::new(),
Applicability::MachineApplicable,
)
.emit();

// Try again without unmatched angle bracket characters.
self.parse_angle_args()
// Try again without unmatched angle bracket characters.
self.parse_angle_args()
}
}
Err(e) => Err(e),
}
Expand Down
3 changes: 3 additions & 0 deletions src/test/ui/parser/issue-84104.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// error-pattern: this file contains an unclosed delimiter
// error-pattern: expected one of
#[i=i::<ښܖ<
16 changes: 16 additions & 0 deletions src/test/ui/parser/issue-84104.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
error: this file contains an unclosed delimiter
--> $DIR/issue-84104.rs:3:13
|
LL | #[i=i::<ښܖ<
| - ^
| |
| unclosed delimiter

error: expected one of `>`, a const expression, lifetime, or type, found `]`
--> $DIR/issue-84104.rs:3:13
|
LL | #[i=i::<ښܖ<
| ^ expected one of `>`, a const expression, lifetime, or type

error: aborting due to 2 previous errors

9 changes: 9 additions & 0 deletions src/test/ui/parser/unmatched-langle-1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Check that a suggestion is issued if there are too many `<`s in a
// generic argument list, and that the parser recovers properly.

fn main() {
foo::<<<<Ty<i32>>();
//~^ ERROR: unmatched angle brackets
//~| ERROR: cannot find function `foo` in this scope [E0425]
//~| ERROR: cannot find type `Ty` in this scope [E0412]
}
22 changes: 22 additions & 0 deletions src/test/ui/parser/unmatched-langle-1.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
error: unmatched angle brackets
--> $DIR/unmatched-langle-1.rs:5:10
|
LL | foo::<<<<Ty<i32>>();
| ^^^ help: remove extra angle brackets

error[E0425]: cannot find function `foo` in this scope
--> $DIR/unmatched-langle-1.rs:5:5
|
LL | foo::<<<<Ty<i32>>();
| ^^^ not found in this scope

error[E0412]: cannot find type `Ty` in this scope
--> $DIR/unmatched-langle-1.rs:5:14
|
LL | foo::<<<<Ty<i32>>();
| ^^ not found in this scope

error: aborting due to 3 previous errors

Some errors have detailed explanations: E0412, E0425.
For more information about an error, try `rustc --explain E0412`.
15 changes: 15 additions & 0 deletions src/test/ui/parser/unmatched-langle-2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// When there are too many opening `<`s, the compiler would previously
// suggest nonsense if the `<`s were interspersed with other tokens:
//
// error: unmatched angle brackets
// --> unmatched-langle.rs:2:10
// |
// 2 | foo::<Ty<<<i32>();
// | ^^^ help: remove extra angle brackets
//
// This test makes sure that this is no longer happening.

fn main() {
foo::<Ty<<<i32>();
//~^ ERROR: expected `::`, found `(`
}
8 changes: 8 additions & 0 deletions src/test/ui/parser/unmatched-langle-2.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
error: expected `::`, found `(`
--> $DIR/unmatched-langle-2.rs:13:20
|
LL | foo::<Ty<<<i32>();
| ^ expected `::`

error: aborting due to previous error

0 comments on commit 6a6a605

Please sign in to comment.