Skip to content

Commit 58f6aaa

Browse files
committed
Improve diagnostics for parenthesized type arguments
1 parent 1c580bc commit 58f6aaa

7 files changed

+160
-2
lines changed

compiler/rustc_parse/src/parser/path.rs

+84-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
22
use super::{Parser, Restrictions, TokenType};
33
use crate::errors::PathSingleColon;
4+
use crate::parser::{CommaRecoveryMode, RecoverColon, RecoverComma};
45
use crate::{errors, maybe_whole};
56
use ast::token::IdentIsRaw;
67
use rustc_ast::ptr::P;
@@ -10,7 +11,7 @@ use rustc_ast::{
1011
AssocConstraintKind, BlockCheckMode, GenericArg, GenericArgs, Generics, ParenthesizedArgs,
1112
Path, PathSegment, QSelf,
1213
};
13-
use rustc_errors::{Applicability, PResult};
14+
use rustc_errors::{Applicability, Diag, PResult};
1415
use rustc_span::symbol::{kw, sym, Ident};
1516
use rustc_span::{BytePos, Span};
1617
use std::mem;
@@ -373,7 +374,38 @@ impl<'a> Parser<'a> {
373374
.into()
374375
} else {
375376
// `(T, U) -> R`
376-
let (inputs, _) = self.parse_paren_comma_seq(|p| p.parse_ty())?;
377+
378+
let prev_token_before_parsing = self.prev_token.clone();
379+
let token_before_parsing = self.token.clone();
380+
let mut snapshot = None;
381+
if self.may_recover()
382+
&& prev_token_before_parsing.kind == token::ModSep
383+
&& (style == PathStyle::Expr && self.token.can_begin_expr()
384+
|| style == PathStyle::Pat && self.token.can_begin_pattern())
385+
{
386+
snapshot = Some(self.create_snapshot_for_diagnostic());
387+
}
388+
389+
let (inputs, _) = match self.parse_paren_comma_seq(|p| p.parse_ty()) {
390+
Ok(output) => output,
391+
Err(mut error) if prev_token_before_parsing.kind == token::ModSep => {
392+
error.span_label(
393+
prev_token_before_parsing.span.to(token_before_parsing.span),
394+
"while parsing this parenthesized list of type arguments starting here",
395+
);
396+
397+
if let Some(mut snapshot) = snapshot {
398+
snapshot.recover_fn_call_leading_path_sep(
399+
style,
400+
prev_token_before_parsing,
401+
&mut error,
402+
)
403+
}
404+
405+
return Err(error);
406+
}
407+
Err(error) => return Err(error),
408+
};
377409
let inputs_span = lo.to(self.prev_token.span);
378410
let output =
379411
self.parse_ret_ty(AllowPlus::No, RecoverQPath::No, RecoverReturnSign::No)?;
@@ -399,6 +431,56 @@ impl<'a> Parser<'a> {
399431
}
400432
}
401433

434+
/// Recover `$path::(...)` as `$path(...)`.
435+
///
436+
/// ```ignore (diagnostics)
437+
/// foo::(420, "bar")
438+
/// ^^ remove extra separator to make the function call
439+
/// // or
440+
/// match x {
441+
/// Foo::(420, "bar") => { ... },
442+
/// ^^ remove extra separator to turn this into tuple struct pattern
443+
/// _ => { ... },
444+
/// }
445+
/// ```
446+
fn recover_fn_call_leading_path_sep(
447+
&mut self,
448+
style: PathStyle,
449+
prev_token_before_parsing: Token,
450+
error: &mut Diag<'_>,
451+
) {
452+
if ((style == PathStyle::Expr && self.parse_paren_comma_seq(|p| p.parse_expr()).is_ok())
453+
|| (style == PathStyle::Pat
454+
&& self
455+
.parse_paren_comma_seq(|p| {
456+
p.parse_pat_allow_top_alt(
457+
None,
458+
RecoverComma::No,
459+
RecoverColon::No,
460+
CommaRecoveryMode::LikelyTuple,
461+
)
462+
})
463+
.is_ok()))
464+
&& !matches!(self.token.kind, token::ModSep | token::RArrow)
465+
{
466+
error.span_suggestion_verbose(
467+
prev_token_before_parsing.span,
468+
format!(
469+
"consider removing the `::` here to {}",
470+
match style {
471+
PathStyle::Expr => "call the expression",
472+
PathStyle::Pat => "turn this into a tuple struct pattern",
473+
_ => {
474+
return;
475+
}
476+
}
477+
),
478+
"",
479+
Applicability::MaybeIncorrect,
480+
);
481+
}
482+
}
483+
402484
/// Parses generic args (within a path segment) with recovery for extra leading angle brackets.
403485
/// For the purposes of understanding the parsing logic of generic arguments, this function
404486
/// can be thought of being the same as just calling `self.parse_angle_args()` if the source
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
fn main() {
2+
foo::( //~ HELP: consider removing the `::` here to call the expression
3+
//~^ NOTE: while parsing this parenthesized list of type arguments starting
4+
bar(x, y, z),
5+
bar(x, y, z),
6+
bar(x, y, z),
7+
bar(x, y, z),
8+
bar(x, y, z),
9+
bar(x, y, z),
10+
bar(x, y, z),
11+
baz("test"), //~ ERROR: expected type, found `"test"`
12+
//~^ NOTE: expected type
13+
)
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error: expected type, found `"test"`
2+
--> $DIR/diagnostics-parenthesized-type-arguments-issue-120892-1.rs:11:9
3+
|
4+
LL | foo::(
5+
| --- while parsing this parenthesized list of type arguments starting here
6+
...
7+
LL | baz("test"),
8+
| ^^^^^^ expected type
9+
|
10+
help: consider removing the `::` here to call the expression
11+
|
12+
LL - foo::(
13+
LL + foo(
14+
|
15+
16+
error: aborting due to 1 previous error
17+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
fn main() {
2+
foo::/* definitely not harmful comment */(123, "foo") -> (u32); //~ ERROR: expected type, found `123`
3+
//~^ NOTE: while parsing this parenthesized list of type arguments starting
4+
//~^^ NOTE: expected type
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: expected type, found `123`
2+
--> $DIR/diagnostics-parenthesized-type-arguments-issue-120892-2.rs:2:45
3+
|
4+
LL | foo::/* definitely not harmful comment */(123, "foo") -> (u32);
5+
| ---------------------------------------^^^ expected type
6+
| |
7+
| while parsing this parenthesized list of type arguments starting here
8+
9+
error: aborting due to 1 previous error
10+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
struct Foo(u32, u32);
2+
impl Foo {
3+
fn foo(&self) {
4+
match *self {
5+
Foo::(1, 2) => {}, //~ HELP: consider removing the `::` here to turn this into a tuple struct pattern
6+
//~^ NOTE: while parsing this parenthesized list of type arguments starting
7+
//~^^ ERROR: expected type, found `1`
8+
//~^^^ NOTE: expected type
9+
_ => {},
10+
}
11+
}
12+
}
13+
14+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: expected type, found `1`
2+
--> $DIR/diagnostics-parenthesized-type-arguments-issue-120892-3.rs:5:19
3+
|
4+
LL | Foo::(1, 2) => {},
5+
| ---^ expected type
6+
| |
7+
| while parsing this parenthesized list of type arguments starting here
8+
|
9+
help: consider removing the `::` here to turn this into a tuple struct pattern
10+
|
11+
LL - Foo::(1, 2) => {},
12+
LL + Foo(1, 2) => {},
13+
|
14+
15+
error: aborting due to 1 previous error
16+

0 commit comments

Comments
 (0)