Navigation Menu

Skip to content

Commit

Permalink
Add error for trailing angle brackets.
Browse files Browse the repository at this point in the history
This commit adds a error (and accompanying machine applicable
suggestion) for trailing angle brackets on function calls with a
turbofish.
  • Loading branch information
davidtwco committed Jan 21, 2019
1 parent 33b0b71 commit 6c399d1
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 0 deletions.
14 changes: 14 additions & 0 deletions src/libsyntax/ast.rs
Expand Up @@ -140,6 +140,20 @@ pub enum GenericArgs {
}

impl GenericArgs {
pub fn is_parenthesized(&self) -> bool {
match *self {
Parenthesized(..) => true,
_ => false,
}
}

pub fn is_angle_bracketed(&self) -> bool {
match *self {
AngleBracketed(..) => true,
_ => false,
}
}

pub fn span(&self) -> Span {
match *self {
AngleBracketed(ref data) => data.span,
Expand Down
97 changes: 97 additions & 0 deletions src/libsyntax/parse/parser.rs
Expand Up @@ -2757,6 +2757,8 @@ impl<'a> Parser<'a> {
// Assuming we have just parsed `.`, continue parsing into an expression.
fn parse_dot_suffix(&mut self, self_arg: P<Expr>, lo: Span) -> PResult<'a, P<Expr>> {
let segment = self.parse_path_segment(PathStyle::Expr, true)?;
self.check_trailing_angle_brackets(&segment);

Ok(match self.token {
token::OpenDelim(token::Paren) => {
// Method call `expr.f()`
Expand Down Expand Up @@ -2784,6 +2786,101 @@ impl<'a> Parser<'a> {
})
}

/// This function checks if there are trailing angle brackets and produces
/// a diagnostic to suggest removing them.
///
/// ```ignore (diagnostic)
/// let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>>>();
/// ^^ help: remove extra angle brackets
/// ```
fn check_trailing_angle_brackets(&mut self, segment: &PathSegment) {
// This function is intended to be invoked from `parse_dot_suffix` where there are two
// cases:
//
// - A field access (eg. `x.foo`)
// - A method call (eg. `x.foo()`)
//
// This function is called after parsing `.foo` and before parsing any parenthesis (if
// present). This includes any angle bracket arguments, such as `.foo::<u32>`.

// We only care about trailing angle brackets if we previously parsed angle bracket
// arguments. This helps stop us incorrectly suggesting that extra angle brackets be
// removed in this case:
//
// `x.foo >> (3)` (where `x.foo` is a `u32` for example)
//
// This case is particularly tricky as we won't notice it just looking at the tokens -
// it will appear the same (in terms of upcoming tokens) as below (since the `::<u32>` will
// have already been parsed):
//
// `x.foo::<u32>>>(3)`
let parsed_angle_bracket_args = segment.args
.as_ref()
.map(|args| args.is_angle_bracketed())
.unwrap_or(false);

debug!(
"check_trailing_angle_brackets: parsed_angle_bracket_args={:?}",
parsed_angle_bracket_args,
);
if !parsed_angle_bracket_args {
return;
}

// Keep the span at the start so we can highlight the sequence of `>` characters to be
// removed.
let lo = self.span;

// We need to look-ahead to see if we have `>` characters without moving the cursor forward
// (since we might have the field access case and the characters we're eating are
// actual operators and not trailing characters - ie `x.foo >> 3`).
let mut position = 0;

// The first tokens we will encounter are shift right tokens (`>>`) since pairs of `>`
// characters will have been grouped together by the tokenizer.
let mut number_of_shr = 0;
while self.look_ahead(position, |t| *t == token::BinOp(token::BinOpToken::Shr)) {
number_of_shr += 1;
position += 1;
}

// Afterwards, there will be at most one `>` character remaining (more than one and it'd
// have shown up as a `>>`).
let encountered_gt = self.look_ahead(position, |t| *t == token::Gt);
if encountered_gt {
position += 1;
}

// If we didn't find any trailing `>>` characters or a trailing `>`, then we have
// nothing to error about.
debug!(
"check_trailing_angle_brackets: encountered_gt={:?} number_of_shr={:?}",
encountered_gt, number_of_shr,
);
if !encountered_gt && number_of_shr < 1 {
return;
}

// Finally, double check that we have a left parenthesis next as otherwise this is the
// field case.
if self.look_ahead(position, |t| *t == token::OpenDelim(token::Paren)) {
// Eat from where we started until the left parenthesis so that parsing can continue
// as if we didn't have those extra angle brackets.
self.eat_to_tokens(&[&token::OpenDelim(token::Paren)]);
let span = lo.until(self.span);

self.diagnostic()
.struct_span_err(span, "unmatched angle bracket")
.span_suggestion_with_applicability(
span,
"remove extra angle bracket",
String::new(),
Applicability::MachineApplicable,
)
.emit();
}
}

fn parse_dot_or_call_expr_with_(&mut self, e0: P<Expr>, lo: Span) -> PResult<'a, P<Expr>> {
let mut e = e0;
let mut hi;
Expand Down
16 changes: 16 additions & 0 deletions src/test/ui/issues/issue-54521-1.rs
@@ -0,0 +1,16 @@
// compile-pass

// This test checks that the `remove extra angle brackets` error doesn't happen for some
// potential edge-cases..

struct X {
len: u32,
}

fn main() {
let x = X { len: 3 };

let _ = x.len > (3);

let _ = x.len >> (3);
}
22 changes: 22 additions & 0 deletions src/test/ui/issues/issue-54521.fixed
@@ -0,0 +1,22 @@
// run-rustfix

// This test checks that the following error is emitted and the suggestion works:
//
// ```
// let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>>>();
// ^^ help: remove extra angle brackets
// ```

fn main() {
let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>();
//~^ ERROR unmatched angle bracket

let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>();
//~^ ERROR unmatched angle bracket

let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>();
//~^ ERROR unmatched angle bracket

let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>();
//~^ ERROR unmatched angle bracket
}
22 changes: 22 additions & 0 deletions src/test/ui/issues/issue-54521.rs
@@ -0,0 +1,22 @@
// run-rustfix

// This test checks that the following error is emitted and the suggestion works:
//
// ```
// let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>>>();
// ^^ help: remove extra angle brackets
// ```

fn main() {
let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>>>>>();
//~^ ERROR unmatched angle bracket

let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>>>>();
//~^ ERROR unmatched angle bracket

let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>>>();
//~^ ERROR unmatched angle bracket

let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>>();
//~^ ERROR unmatched angle bracket
}
26 changes: 26 additions & 0 deletions src/test/ui/issues/issue-54521.stderr
@@ -0,0 +1,26 @@
error: unmatched angle bracket
--> $DIR/issue-54521.rs:11:60
|
LL | let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>>>>>();
| ^^^^ help: remove extra angle bracket

error: unmatched angle bracket
--> $DIR/issue-54521.rs:14:60
|
LL | let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>>>>();
| ^^^ help: remove extra angle bracket

error: unmatched angle bracket
--> $DIR/issue-54521.rs:17:60
|
LL | let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>>>();
| ^^ help: remove extra angle bracket

error: unmatched angle bracket
--> $DIR/issue-54521.rs:20:60
|
LL | let _ = vec![1, 2, 3].into_iter().collect::<Vec<usize>>>();
| ^ help: remove extra angle bracket

error: aborting due to 4 previous errors

0 comments on commit 6c399d1

Please sign in to comment.