From 20f4e5d9c24809031fd6631436f0a7a5b075fe40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 23 Jul 2020 09:34:07 -0700 Subject: [PATCH 1/2] Detect turbofish missing surrounding angle brackets --- src/librustc_parse/parser/diagnostics.rs | 42 ++++++++++++++++++- src/librustc_parse/parser/expr.rs | 3 +- src/librustc_parse/parser/path.rs | 2 +- ...sing-turbofish-surrounding-angle-braket.rs | 4 ++ ...-turbofish-surrounding-angle-braket.stderr | 13 ++++++ 5 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 src/test/ui/suggestions/recover-missing-turbofish-surrounding-angle-braket.rs create mode 100644 src/test/ui/suggestions/recover-missing-turbofish-surrounding-angle-braket.stderr diff --git a/src/librustc_parse/parser/diagnostics.rs b/src/librustc_parse/parser/diagnostics.rs index 3244b35e89b0e..f84f54054acdb 100644 --- a/src/librustc_parse/parser/diagnostics.rs +++ b/src/librustc_parse/parser/diagnostics.rs @@ -2,7 +2,9 @@ use super::ty::AllowPlus; use super::{BlockMode, Parser, PathStyle, SemiColonMode, SeqSep, TokenExpectType, TokenType}; use rustc_ast::ast::{self, BinOpKind, BindingMode, BlockCheckMode, Expr, ExprKind, Item, Param}; -use rustc_ast::ast::{AttrVec, ItemKind, Mutability, Pat, PatKind, PathSegment, QSelf, Ty, TyKind}; +use rustc_ast::ast::{ + AngleBracketedArgs, AttrVec, ItemKind, Mutability, Pat, PatKind, PathSegment, QSelf, Ty, TyKind, +}; use rustc_ast::ptr::P; use rustc_ast::token::{self, Lit, LitKind, TokenKind}; use rustc_ast::util::parser::AssocOp; @@ -488,6 +490,44 @@ impl<'a> Parser<'a> { false } + /// Check if a method call with an intended turbofish has been written without surrounding + /// angle brackets. + pub(super) fn check_turbofish_missing_angle_brackets(&mut self, segment: &mut PathSegment) { + if token::ModSep == self.token.kind && segment.args.is_none() { + let snapshot = self.clone(); + self.bump(); + let lo = self.token.span; + match self.parse_angle_args() { + Ok(args) if self.token.kind == token::OpenDelim(token::Paren) => { + // Recover from bad turbofish: `foo.collect::Vec<_>()`. + let span = lo.to(self.prev_token.span); + let args = AngleBracketedArgs { args, span }.into(); + segment.args = args; + self.struct_span_err( + span, + "generic parameters without surrounding angle brackets", + ) + .multipart_suggestion( + "surround the type parameters with angle brackets", + vec![ + (span.shrink_to_lo(), "<".to_string()), + (span.shrink_to_hi(), ">".to_string()), + ], + Applicability::MachineApplicable, + ) + .emit(); + } + Ok(_) => { + *self = snapshot; + } + Err(mut err) => { + err.cancel(); + *self = snapshot; + } + } + } + } + /// Check to see if a pair of chained operators looks like an attempt at chained comparison, /// e.g. `1 < x <= 3`. If so, suggest either splitting the comparison into two, or /// parenthesising the leftmost comparison. diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs index 3926122606e6d..d06b172bc1484 100644 --- a/src/librustc_parse/parser/expr.rs +++ b/src/librustc_parse/parser/expr.rs @@ -909,8 +909,9 @@ impl<'a> Parser<'a> { } let fn_span_lo = self.token.span; - let segment = self.parse_path_segment(PathStyle::Expr)?; + let mut segment = self.parse_path_segment(PathStyle::Expr)?; self.check_trailing_angle_brackets(&segment, &[&token::OpenDelim(token::Paren)]); + self.check_turbofish_missing_angle_brackets(&mut segment); if self.check(&token::OpenDelim(token::Paren)) { // Method call `expr.f()` diff --git a/src/librustc_parse/parser/path.rs b/src/librustc_parse/parser/path.rs index 67e9b3af4a8cf..3dcefd362574b 100644 --- a/src/librustc_parse/parser/path.rs +++ b/src/librustc_parse/parser/path.rs @@ -387,7 +387,7 @@ impl<'a> Parser<'a> { /// Parses (possibly empty) list of generic arguments / associated item constraints, /// possibly including trailing comma. - fn parse_angle_args(&mut self) -> PResult<'a, Vec> { + pub(super) fn parse_angle_args(&mut self) -> PResult<'a, Vec> { let mut args = Vec::new(); while let Some(arg) = self.parse_angle_arg()? { args.push(arg); diff --git a/src/test/ui/suggestions/recover-missing-turbofish-surrounding-angle-braket.rs b/src/test/ui/suggestions/recover-missing-turbofish-surrounding-angle-braket.rs new file mode 100644 index 0000000000000..63fbee9f0f526 --- /dev/null +++ b/src/test/ui/suggestions/recover-missing-turbofish-surrounding-angle-braket.rs @@ -0,0 +1,4 @@ +fn main() { + let _ = vec![1, 2, 3].into_iter().collect::Vec<_>(); + //~^ ERROR generic parameters without surrounding angle brackets +} diff --git a/src/test/ui/suggestions/recover-missing-turbofish-surrounding-angle-braket.stderr b/src/test/ui/suggestions/recover-missing-turbofish-surrounding-angle-braket.stderr new file mode 100644 index 0000000000000..ad48a0c7a5cf7 --- /dev/null +++ b/src/test/ui/suggestions/recover-missing-turbofish-surrounding-angle-braket.stderr @@ -0,0 +1,13 @@ +error: generic parameters without surrounding angle brackets + --> $DIR/recover-missing-turbofish-surrounding-angle-braket.rs:2:48 + | +LL | let _ = vec![1, 2, 3].into_iter().collect::Vec<_>(); + | ^^^^^^ + | +help: surround the type parameters with angle brackets + | +LL | let _ = vec![1, 2, 3].into_iter().collect::>(); + | ^ ^ + +error: aborting due to previous error + From d090e5ed3327a24c17754be7e224b5f2345aa622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 23 Jul 2020 16:25:39 -0700 Subject: [PATCH 2/2] Account for trailing closing angle brackets --- src/librustc_parse/parser/diagnostics.rs | 57 ++++++++++++------- ...sing-turbofish-surrounding-angle-braket.rs | 6 ++ ...-turbofish-surrounding-angle-braket.stderr | 35 +++++++++++- 3 files changed, 75 insertions(+), 23 deletions(-) diff --git a/src/librustc_parse/parser/diagnostics.rs b/src/librustc_parse/parser/diagnostics.rs index f84f54054acdb..609a0c961e971 100644 --- a/src/librustc_parse/parser/diagnostics.rs +++ b/src/librustc_parse/parser/diagnostics.rs @@ -1,9 +1,9 @@ use super::ty::AllowPlus; use super::{BlockMode, Parser, PathStyle, SemiColonMode, SeqSep, TokenExpectType, TokenType}; -use rustc_ast::ast::{self, BinOpKind, BindingMode, BlockCheckMode, Expr, ExprKind, Item, Param}; use rustc_ast::ast::{ - AngleBracketedArgs, AttrVec, ItemKind, Mutability, Pat, PatKind, PathSegment, QSelf, Ty, TyKind, + self, AngleBracketedArgs, AttrVec, BinOpKind, BindingMode, BlockCheckMode, Expr, ExprKind, + Item, ItemKind, Mutability, Param, Pat, PatKind, PathSegment, QSelf, Ty, TyKind, }; use rustc_ast::ptr::P; use rustc_ast::token::{self, Lit, LitKind, TokenKind}; @@ -498,29 +498,42 @@ impl<'a> Parser<'a> { self.bump(); let lo = self.token.span; match self.parse_angle_args() { - Ok(args) if self.token.kind == token::OpenDelim(token::Paren) => { - // Recover from bad turbofish: `foo.collect::Vec<_>()`. + Ok(args) => { let span = lo.to(self.prev_token.span); - let args = AngleBracketedArgs { args, span }.into(); - segment.args = args; - self.struct_span_err( - span, - "generic parameters without surrounding angle brackets", - ) - .multipart_suggestion( - "surround the type parameters with angle brackets", - vec![ - (span.shrink_to_lo(), "<".to_string()), - (span.shrink_to_hi(), ">".to_string()), - ], - Applicability::MachineApplicable, - ) - .emit(); - } - Ok(_) => { - *self = snapshot; + // Detect trailing `>` like in `x.collect::Vec<_>>()`. + let mut trailing_span = self.prev_token.span.shrink_to_hi(); + while self.token.kind == token::BinOp(token::Shr) + || self.token.kind == token::Gt + { + trailing_span = trailing_span.to(self.token.span); + self.bump(); + } + if self.token.kind == token::OpenDelim(token::Paren) { + // Recover from bad turbofish: `foo.collect::Vec<_>()`. + let args = AngleBracketedArgs { args, span }.into(); + segment.args = args; + + self.struct_span_err( + span, + "generic parameters without surrounding angle brackets", + ) + .multipart_suggestion( + "surround the type parameters with angle brackets", + vec![ + (span.shrink_to_lo(), "<".to_string()), + (trailing_span, ">".to_string()), + ], + Applicability::MachineApplicable, + ) + .emit(); + } else { + // This doesn't look like an invalid turbofish, can't recover parse state. + *self = snapshot; + } } Err(mut err) => { + // We could't parse generic parameters, unlikely to be a turbofish. Rely on + // generic parse error instead. err.cancel(); *self = snapshot; } diff --git a/src/test/ui/suggestions/recover-missing-turbofish-surrounding-angle-braket.rs b/src/test/ui/suggestions/recover-missing-turbofish-surrounding-angle-braket.rs index 63fbee9f0f526..333dce390461c 100644 --- a/src/test/ui/suggestions/recover-missing-turbofish-surrounding-angle-braket.rs +++ b/src/test/ui/suggestions/recover-missing-turbofish-surrounding-angle-braket.rs @@ -1,4 +1,10 @@ fn main() { let _ = vec![1, 2, 3].into_iter().collect::Vec<_>(); //~^ ERROR generic parameters without surrounding angle brackets + let _ = vec![1, 2, 3].into_iter().collect::Vec<_>>>>(); + //~^ ERROR generic parameters without surrounding angle brackets + let _ = vec![1, 2, 3].into_iter().collect::Vec<_>>>(); + //~^ ERROR generic parameters without surrounding angle brackets + let _ = vec![1, 2, 3].into_iter().collect::Vec<_>>(); + //~^ ERROR generic parameters without surrounding angle brackets } diff --git a/src/test/ui/suggestions/recover-missing-turbofish-surrounding-angle-braket.stderr b/src/test/ui/suggestions/recover-missing-turbofish-surrounding-angle-braket.stderr index ad48a0c7a5cf7..981f95749d3ba 100644 --- a/src/test/ui/suggestions/recover-missing-turbofish-surrounding-angle-braket.stderr +++ b/src/test/ui/suggestions/recover-missing-turbofish-surrounding-angle-braket.stderr @@ -9,5 +9,38 @@ help: surround the type parameters with angle brackets LL | let _ = vec![1, 2, 3].into_iter().collect::>(); | ^ ^ -error: aborting due to previous error +error: generic parameters without surrounding angle brackets + --> $DIR/recover-missing-turbofish-surrounding-angle-braket.rs:4:48 + | +LL | let _ = vec![1, 2, 3].into_iter().collect::Vec<_>>>>(); + | ^^^^^^ + | +help: surround the type parameters with angle brackets + | +LL | let _ = vec![1, 2, 3].into_iter().collect::>(); + | ^ ^ + +error: generic parameters without surrounding angle brackets + --> $DIR/recover-missing-turbofish-surrounding-angle-braket.rs:6:48 + | +LL | let _ = vec![1, 2, 3].into_iter().collect::Vec<_>>>(); + | ^^^^^^ + | +help: surround the type parameters with angle brackets + | +LL | let _ = vec![1, 2, 3].into_iter().collect::>(); + | ^ ^ + +error: generic parameters without surrounding angle brackets + --> $DIR/recover-missing-turbofish-surrounding-angle-braket.rs:8:48 + | +LL | let _ = vec![1, 2, 3].into_iter().collect::Vec<_>>(); + | ^^^^^^ + | +help: surround the type parameters with angle brackets + | +LL | let _ = vec![1, 2, 3].into_iter().collect::>(); + | ^ ^ + +error: aborting due to 4 previous errors