Skip to content

Commit 6a6a605

Browse files
committed
Fix handling of unmatched angle brackets in parser
1 parent 4e219e6 commit 6a6a605

File tree

7 files changed

+120
-37
lines changed

7 files changed

+120
-37
lines changed

compiler/rustc_parse/src/parser/path.rs

+47-37
Original file line numberDiff line numberDiff line change
@@ -352,49 +352,59 @@ impl<'a> Parser<'a> {
352352
debug!("parse_generic_args_with_leading_angle_bracket_recovery: (snapshotting)");
353353
match self.parse_angle_args() {
354354
Ok(args) => Ok(args),
355-
Err(ref mut e) if is_first_invocation && self.unmatched_angle_bracket_count > 0 => {
356-
// Cancel error from being unable to find `>`. We know the error
357-
// must have been this due to a non-zero unmatched angle bracket
358-
// count.
359-
e.cancel();
360-
355+
Err(mut e) if is_first_invocation && self.unmatched_angle_bracket_count > 0 => {
361356
// Swap `self` with our backup of the parser state before attempting to parse
362357
// generic arguments.
363358
let snapshot = mem::replace(self, snapshot.unwrap());
364359

365-
debug!(
366-
"parse_generic_args_with_leading_angle_bracket_recovery: (snapshot failure) \
367-
snapshot.count={:?}",
368-
snapshot.unmatched_angle_bracket_count,
369-
);
370-
371360
// Eat the unmatched angle brackets.
372-
for _ in 0..snapshot.unmatched_angle_bracket_count {
373-
self.eat_lt();
374-
}
375-
376-
// Make a span over ${unmatched angle bracket count} characters.
377-
let span = lo.with_hi(lo.lo() + BytePos(snapshot.unmatched_angle_bracket_count));
378-
self.struct_span_err(
379-
span,
380-
&format!(
381-
"unmatched angle bracket{}",
382-
pluralize!(snapshot.unmatched_angle_bracket_count)
383-
),
384-
)
385-
.span_suggestion(
386-
span,
387-
&format!(
388-
"remove extra angle bracket{}",
389-
pluralize!(snapshot.unmatched_angle_bracket_count)
390-
),
391-
String::new(),
392-
Applicability::MachineApplicable,
393-
)
394-
.emit();
361+
let all_angle_brackets = (0..snapshot.unmatched_angle_bracket_count)
362+
.fold(true, |a, _| a && self.eat_lt());
363+
364+
if !all_angle_brackets {
365+
// If there are other tokens in between the extraneous `<`s, we cannot simply
366+
// suggest to remove them. This check also prevents us from accidentally ending
367+
// up in the middle of a multibyte character (issue #84104).
368+
let _ = mem::replace(self, snapshot);
369+
Err(e)
370+
} else {
371+
// Cancel error from being unable to find `>`. We know the error
372+
// must have been this due to a non-zero unmatched angle bracket
373+
// count.
374+
e.cancel();
375+
376+
debug!(
377+
"parse_generic_args_with_leading_angle_bracket_recovery: (snapshot failure) \
378+
snapshot.count={:?}",
379+
snapshot.unmatched_angle_bracket_count,
380+
);
381+
382+
// Make a span over ${unmatched angle bracket count} characters.
383+
// This is safe because `all_angle_brackets` ensures that there are only `<`s,
384+
// i.e. no multibyte characters, in this range.
385+
let span =
386+
lo.with_hi(lo.lo() + BytePos(snapshot.unmatched_angle_bracket_count));
387+
self.struct_span_err(
388+
span,
389+
&format!(
390+
"unmatched angle bracket{}",
391+
pluralize!(snapshot.unmatched_angle_bracket_count)
392+
),
393+
)
394+
.span_suggestion(
395+
span,
396+
&format!(
397+
"remove extra angle bracket{}",
398+
pluralize!(snapshot.unmatched_angle_bracket_count)
399+
),
400+
String::new(),
401+
Applicability::MachineApplicable,
402+
)
403+
.emit();
395404

396-
// Try again without unmatched angle bracket characters.
397-
self.parse_angle_args()
405+
// Try again without unmatched angle bracket characters.
406+
self.parse_angle_args()
407+
}
398408
}
399409
Err(e) => Err(e),
400410
}

src/test/ui/parser/issue-84104.rs

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// error-pattern: this file contains an unclosed delimiter
2+
// error-pattern: expected one of
3+
#[i=i::<ښܖ<

src/test/ui/parser/issue-84104.stderr

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: this file contains an unclosed delimiter
2+
--> $DIR/issue-84104.rs:3:13
3+
|
4+
LL | #[i=i::<ښܖ<
5+
| - ^
6+
| |
7+
| unclosed delimiter
8+
9+
error: expected one of `>`, a const expression, lifetime, or type, found `]`
10+
--> $DIR/issue-84104.rs:3:13
11+
|
12+
LL | #[i=i::<ښܖ<
13+
| ^ expected one of `>`, a const expression, lifetime, or type
14+
15+
error: aborting due to 2 previous errors
16+
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Check that a suggestion is issued if there are too many `<`s in a
2+
// generic argument list, and that the parser recovers properly.
3+
4+
fn main() {
5+
foo::<<<<Ty<i32>>();
6+
//~^ ERROR: unmatched angle brackets
7+
//~| ERROR: cannot find function `foo` in this scope [E0425]
8+
//~| ERROR: cannot find type `Ty` in this scope [E0412]
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error: unmatched angle brackets
2+
--> $DIR/unmatched-langle-1.rs:5:10
3+
|
4+
LL | foo::<<<<Ty<i32>>();
5+
| ^^^ help: remove extra angle brackets
6+
7+
error[E0425]: cannot find function `foo` in this scope
8+
--> $DIR/unmatched-langle-1.rs:5:5
9+
|
10+
LL | foo::<<<<Ty<i32>>();
11+
| ^^^ not found in this scope
12+
13+
error[E0412]: cannot find type `Ty` in this scope
14+
--> $DIR/unmatched-langle-1.rs:5:14
15+
|
16+
LL | foo::<<<<Ty<i32>>();
17+
| ^^ not found in this scope
18+
19+
error: aborting due to 3 previous errors
20+
21+
Some errors have detailed explanations: E0412, E0425.
22+
For more information about an error, try `rustc --explain E0412`.
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// When there are too many opening `<`s, the compiler would previously
2+
// suggest nonsense if the `<`s were interspersed with other tokens:
3+
//
4+
// error: unmatched angle brackets
5+
// --> unmatched-langle.rs:2:10
6+
// |
7+
// 2 | foo::<Ty<<<i32>();
8+
// | ^^^ help: remove extra angle brackets
9+
//
10+
// This test makes sure that this is no longer happening.
11+
12+
fn main() {
13+
foo::<Ty<<<i32>();
14+
//~^ ERROR: expected `::`, found `(`
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: expected `::`, found `(`
2+
--> $DIR/unmatched-langle-2.rs:13:20
3+
|
4+
LL | foo::<Ty<<<i32>();
5+
| ^ expected `::`
6+
7+
error: aborting due to previous error
8+

0 commit comments

Comments
 (0)