Skip to content

Commit 6c399d1

Browse files
committed
Add error for trailing angle brackets.
This commit adds a error (and accompanying machine applicable suggestion) for trailing angle brackets on function calls with a turbofish.
1 parent 33b0b71 commit 6c399d1

File tree

6 files changed

+197
-0
lines changed

6 files changed

+197
-0
lines changed

src/libsyntax/ast.rs

+14
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,20 @@ pub enum GenericArgs {
140140
}
141141

142142
impl GenericArgs {
143+
pub fn is_parenthesized(&self) -> bool {
144+
match *self {
145+
Parenthesized(..) => true,
146+
_ => false,
147+
}
148+
}
149+
150+
pub fn is_angle_bracketed(&self) -> bool {
151+
match *self {
152+
AngleBracketed(..) => true,
153+
_ => false,
154+
}
155+
}
156+
143157
pub fn span(&self) -> Span {
144158
match *self {
145159
AngleBracketed(ref data) => data.span,

src/libsyntax/parse/parser.rs

+97
Original file line numberDiff line numberDiff line change
@@ -2757,6 +2757,8 @@ impl<'a> Parser<'a> {
27572757
// Assuming we have just parsed `.`, continue parsing into an expression.
27582758
fn parse_dot_suffix(&mut self, self_arg: P<Expr>, lo: Span) -> PResult<'a, P<Expr>> {
27592759
let segment = self.parse_path_segment(PathStyle::Expr, true)?;
2760+
self.check_trailing_angle_brackets(&segment);
2761+
27602762
Ok(match self.token {
27612763
token::OpenDelim(token::Paren) => {
27622764
// Method call `expr.f()`
@@ -2784,6 +2786,101 @@ impl<'a> Parser<'a> {
27842786
})
27852787
}
27862788

2789+
/// This function checks if there are trailing angle brackets and produces
2790+
/// a diagnostic to suggest removing them.
2791+
///
2792+
/// ```ignore (diagnostic)
2793+
/// let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>>>();
2794+
/// ^^ help: remove extra angle brackets
2795+
/// ```
2796+
fn check_trailing_angle_brackets(&mut self, segment: &PathSegment) {
2797+
// This function is intended to be invoked from `parse_dot_suffix` where there are two
2798+
// cases:
2799+
//
2800+
// - A field access (eg. `x.foo`)
2801+
// - A method call (eg. `x.foo()`)
2802+
//
2803+
// This function is called after parsing `.foo` and before parsing any parenthesis (if
2804+
// present). This includes any angle bracket arguments, such as `.foo::<u32>`.
2805+
2806+
// We only care about trailing angle brackets if we previously parsed angle bracket
2807+
// arguments. This helps stop us incorrectly suggesting that extra angle brackets be
2808+
// removed in this case:
2809+
//
2810+
// `x.foo >> (3)` (where `x.foo` is a `u32` for example)
2811+
//
2812+
// This case is particularly tricky as we won't notice it just looking at the tokens -
2813+
// it will appear the same (in terms of upcoming tokens) as below (since the `::<u32>` will
2814+
// have already been parsed):
2815+
//
2816+
// `x.foo::<u32>>>(3)`
2817+
let parsed_angle_bracket_args = segment.args
2818+
.as_ref()
2819+
.map(|args| args.is_angle_bracketed())
2820+
.unwrap_or(false);
2821+
2822+
debug!(
2823+
"check_trailing_angle_brackets: parsed_angle_bracket_args={:?}",
2824+
parsed_angle_bracket_args,
2825+
);
2826+
if !parsed_angle_bracket_args {
2827+
return;
2828+
}
2829+
2830+
// Keep the span at the start so we can highlight the sequence of `>` characters to be
2831+
// removed.
2832+
let lo = self.span;
2833+
2834+
// We need to look-ahead to see if we have `>` characters without moving the cursor forward
2835+
// (since we might have the field access case and the characters we're eating are
2836+
// actual operators and not trailing characters - ie `x.foo >> 3`).
2837+
let mut position = 0;
2838+
2839+
// The first tokens we will encounter are shift right tokens (`>>`) since pairs of `>`
2840+
// characters will have been grouped together by the tokenizer.
2841+
let mut number_of_shr = 0;
2842+
while self.look_ahead(position, |t| *t == token::BinOp(token::BinOpToken::Shr)) {
2843+
number_of_shr += 1;
2844+
position += 1;
2845+
}
2846+
2847+
// Afterwards, there will be at most one `>` character remaining (more than one and it'd
2848+
// have shown up as a `>>`).
2849+
let encountered_gt = self.look_ahead(position, |t| *t == token::Gt);
2850+
if encountered_gt {
2851+
position += 1;
2852+
}
2853+
2854+
// If we didn't find any trailing `>>` characters or a trailing `>`, then we have
2855+
// nothing to error about.
2856+
debug!(
2857+
"check_trailing_angle_brackets: encountered_gt={:?} number_of_shr={:?}",
2858+
encountered_gt, number_of_shr,
2859+
);
2860+
if !encountered_gt && number_of_shr < 1 {
2861+
return;
2862+
}
2863+
2864+
// Finally, double check that we have a left parenthesis next as otherwise this is the
2865+
// field case.
2866+
if self.look_ahead(position, |t| *t == token::OpenDelim(token::Paren)) {
2867+
// Eat from where we started until the left parenthesis so that parsing can continue
2868+
// as if we didn't have those extra angle brackets.
2869+
self.eat_to_tokens(&[&token::OpenDelim(token::Paren)]);
2870+
let span = lo.until(self.span);
2871+
2872+
self.diagnostic()
2873+
.struct_span_err(span, "unmatched angle bracket")
2874+
.span_suggestion_with_applicability(
2875+
span,
2876+
"remove extra angle bracket",
2877+
String::new(),
2878+
Applicability::MachineApplicable,
2879+
)
2880+
.emit();
2881+
}
2882+
}
2883+
27872884
fn parse_dot_or_call_expr_with_(&mut self, e0: P<Expr>, lo: Span) -> PResult<'a, P<Expr>> {
27882885
let mut e = e0;
27892886
let mut hi;

src/test/ui/issues/issue-54521-1.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// compile-pass
2+
3+
// This test checks that the `remove extra angle brackets` error doesn't happen for some
4+
// potential edge-cases..
5+
6+
struct X {
7+
len: u32,
8+
}
9+
10+
fn main() {
11+
let x = X { len: 3 };
12+
13+
let _ = x.len > (3);
14+
15+
let _ = x.len >> (3);
16+
}

src/test/ui/issues/issue-54521.fixed

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// run-rustfix
2+
3+
// This test checks that the following error is emitted and the suggestion works:
4+
//
5+
// ```
6+
// let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>>>();
7+
// ^^ help: remove extra angle brackets
8+
// ```
9+
10+
fn main() {
11+
let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>();
12+
//~^ ERROR unmatched angle bracket
13+
14+
let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>();
15+
//~^ ERROR unmatched angle bracket
16+
17+
let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>();
18+
//~^ ERROR unmatched angle bracket
19+
20+
let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>();
21+
//~^ ERROR unmatched angle bracket
22+
}

src/test/ui/issues/issue-54521.rs

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// run-rustfix
2+
3+
// This test checks that the following error is emitted and the suggestion works:
4+
//
5+
// ```
6+
// let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>>>();
7+
// ^^ help: remove extra angle brackets
8+
// ```
9+
10+
fn main() {
11+
let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>>>>>();
12+
//~^ ERROR unmatched angle bracket
13+
14+
let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>>>>();
15+
//~^ ERROR unmatched angle bracket
16+
17+
let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>>>();
18+
//~^ ERROR unmatched angle bracket
19+
20+
let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>>();
21+
//~^ ERROR unmatched angle bracket
22+
}

src/test/ui/issues/issue-54521.stderr

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
error: unmatched angle bracket
2+
--> $DIR/issue-54521.rs:11:60
3+
|
4+
LL | let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>>>>>();
5+
| ^^^^ help: remove extra angle bracket
6+
7+
error: unmatched angle bracket
8+
--> $DIR/issue-54521.rs:14:60
9+
|
10+
LL | let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>>>>();
11+
| ^^^ help: remove extra angle bracket
12+
13+
error: unmatched angle bracket
14+
--> $DIR/issue-54521.rs:17:60
15+
|
16+
LL | let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>>>();
17+
| ^^ help: remove extra angle bracket
18+
19+
error: unmatched angle bracket
20+
--> $DIR/issue-54521.rs:20:60
21+
|
22+
LL | let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>>();
23+
| ^ help: remove extra angle bracket
24+
25+
error: aborting due to 4 previous errors
26+

0 commit comments

Comments
 (0)