Skip to content

Detect turbofish with multiple type params missing leading :: #76171

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions compiler/rustc_parse/src/parser/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,52 @@ impl<'a> Parser<'a> {
}
}

/// When writing a turbofish with multiple type parameters missing the leading `::`, we will
/// encounter a parse error when encountering the first `,`.
pub(super) fn check_mistyped_turbofish_with_multiple_type_params(
&mut self,
mut e: DiagnosticBuilder<'a>,
expr: &mut P<Expr>,
) -> PResult<'a, ()> {
if let ExprKind::Binary(binop, _, _) = &expr.kind {
if let ast::BinOpKind::Lt = binop.node {
if self.eat(&token::Comma) {
let x = self.parse_seq_to_before_end(
&token::Gt,
SeqSep::trailing_allowed(token::Comma),
|p| p.parse_ty(),
);
match x {
Ok((_, _, false)) => {
self.bump(); // `>`
match self.parse_expr() {
Ok(_) => {
e.span_suggestion_verbose(
binop.span.shrink_to_lo(),
"use `::<...>` instead of `<...>` to specify type arguments",
"::".to_string(),
Applicability::MaybeIncorrect,
);
e.emit();
*expr = self.mk_expr_err(expr.span.to(self.prev_token.span));
return Ok(());
}
Err(mut err) => {
err.cancel();
}
}
}
Err(mut err) => {
err.cancel();
}
_ => {}
}
}
}
}
Err(e)
}

/// 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.
Expand Down
26 changes: 20 additions & 6 deletions compiler/rustc_parse/src/parser/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ impl<'a> Parser<'a> {
let mut eat_semi = true;
match stmt.kind {
// Expression without semicolon.
StmtKind::Expr(ref expr)
StmtKind::Expr(ref mut expr)
if self.token != token::Eof && classify::expr_requires_semi_to_be_stmt(expr) =>
{
// Just check for errors and recover; do not eat semicolon yet.
Expand All @@ -387,15 +387,29 @@ impl<'a> Parser<'a> {
);
}
}
e.emit();
self.recover_stmt();
if let Err(mut e) =
self.check_mistyped_turbofish_with_multiple_type_params(e, expr)
{
e.emit();
self.recover_stmt();
}
// Don't complain about type errors in body tail after parse error (#57383).
let sp = expr.span.to(self.prev_token.span);
stmt.kind = StmtKind::Expr(self.mk_expr_err(sp));
*expr = self.mk_expr_err(sp);
}
}
StmtKind::Local(..) => {
self.expect_semi()?;
StmtKind::Local(ref mut local) => {
if let Err(e) = self.expect_semi() {
// We might be at the `,` in `let x = foo<bar, baz>;`. Try to recover.
match &mut local.init {
Some(ref mut expr) => {
self.check_mistyped_turbofish_with_multiple_type_params(e, expr)?;
// We found `foo<bar, baz>`, have we fully recovered?
self.expect_semi()?;
}
None => return Err(e),
}
}
eat_semi = false;
}
StmtKind::Empty => eat_semi = false,
Expand Down
21 changes: 21 additions & 0 deletions src/test/ui/did_you_mean/issue-40396.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,29 @@
fn main() {
(0..13).collect<Vec<i32>>();
//~^ ERROR comparison operators cannot be chained
//~| HELP use `::<...>` instead
Vec<i32>::new();
//~^ ERROR comparison operators cannot be chained
//~| HELP use `::<...>` instead
(0..13).collect<Vec<i32>();
//~^ ERROR comparison operators cannot be chained
//~| HELP use `::<...>` instead
let x = std::collections::HashMap<i128, i128>::new(); //~ ERROR expected one of
//~^ HELP use `::<...>` instead
let x: () = 42; //~ ERROR mismatched types
let x = {
std::collections::HashMap<i128, i128>::new() //~ ERROR expected one of
//~^ HELP use `::<...>` instead
};
let x: () = 42; //~ ERROR mismatched types
let x = {
std::collections::HashMap<i128, i128>::new(); //~ ERROR expected one of
//~^ HELP use `::<...>` instead
let x: () = 42; //~ ERROR mismatched types
};
{
std::collections::HashMap<i128, i128>::new(1, 2); //~ ERROR expected one of
//~^ HELP use `::<...>` instead
let x: () = 32; //~ ERROR mismatched types
};
}
83 changes: 80 additions & 3 deletions src/test/ui/did_you_mean/issue-40396.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ LL | (0..13).collect::<Vec<i32>>();
| ^^

error: comparison operators cannot be chained
--> $DIR/issue-40396.rs:4:8
--> $DIR/issue-40396.rs:5:8
|
LL | Vec<i32>::new();
| ^ ^
Expand All @@ -21,7 +21,7 @@ LL | Vec::<i32>::new();
| ^^

error: comparison operators cannot be chained
--> $DIR/issue-40396.rs:6:20
--> $DIR/issue-40396.rs:8:20
|
LL | (0..13).collect<Vec<i32>();
| ^ ^
Expand All @@ -31,5 +31,82 @@ help: use `::<...>` instead of `<...>` to specify type arguments
LL | (0..13).collect::<Vec<i32>();
| ^^

error: aborting due to 3 previous errors
error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `,`
--> $DIR/issue-40396.rs:11:43
|
LL | let x = std::collections::HashMap<i128, i128>::new();
| ^ expected one of 7 possible tokens
|
help: use `::<...>` instead of `<...>` to specify type arguments
|
LL | let x = std::collections::HashMap::<i128, i128>::new();
| ^^

error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `,`
--> $DIR/issue-40396.rs:15:39
|
LL | std::collections::HashMap<i128, i128>::new()
| ^ expected one of 8 possible tokens
|
help: use `::<...>` instead of `<...>` to specify type arguments
|
LL | std::collections::HashMap::<i128, i128>::new()
| ^^

error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `,`
--> $DIR/issue-40396.rs:20:39
|
LL | std::collections::HashMap<i128, i128>::new();
| ^ expected one of 8 possible tokens
|
help: use `::<...>` instead of `<...>` to specify type arguments
|
LL | std::collections::HashMap::<i128, i128>::new();
| ^^

error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `,`
--> $DIR/issue-40396.rs:25:39
|
LL | std::collections::HashMap<i128, i128>::new(1, 2);
| ^ expected one of 8 possible tokens
|
help: use `::<...>` instead of `<...>` to specify type arguments
|
LL | std::collections::HashMap::<i128, i128>::new(1, 2);
| ^^

error[E0308]: mismatched types
--> $DIR/issue-40396.rs:13:17
|
LL | let x: () = 42;
| -- ^^ expected `()`, found integer
| |
| expected due to this

error[E0308]: mismatched types
--> $DIR/issue-40396.rs:18:17
|
LL | let x: () = 42;
| -- ^^ expected `()`, found integer
| |
| expected due to this

error[E0308]: mismatched types
--> $DIR/issue-40396.rs:22:21
|
LL | let x: () = 42;
| -- ^^ expected `()`, found integer
| |
| expected due to this

error[E0308]: mismatched types
--> $DIR/issue-40396.rs:27:21
|
LL | let x: () = 32;
| -- ^^ expected `()`, found integer
| |
| expected due to this

error: aborting due to 11 previous errors

For more information about this error, try `rustc --explain E0308`.