Skip to content

Commit b3ac88a

Browse files
committed
Recover gracefully from argument with missing type or param name
1 parent aee7012 commit b3ac88a

11 files changed

+161
-28
lines changed

src/librustc/hir/lowering.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2298,7 +2298,7 @@ impl<'a> LoweringContext<'a> {
22982298

22992299
fn lower_arg_source(&mut self, source: &ArgSource) -> hir::ArgSource {
23002300
match source {
2301-
ArgSource::Normal => hir::ArgSource::Normal,
2301+
ArgSource::Normal | ArgSource::Recovery => hir::ArgSource::Normal,
23022302
ArgSource::AsyncFn(pat) => hir::ArgSource::AsyncFn(self.lower_pat(pat)),
23032303
}
23042304
}

src/libsyntax/ast.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1780,6 +1780,8 @@ pub enum ArgSource {
17801780
Normal,
17811781
/// Argument from `async fn` lowering, contains the original binding pattern.
17821782
AsyncFn(P<Pat>),
1783+
/// Placeholder argument caused by incorrect syntax. Used to silence unecessary errors.
1784+
Recovery,
17831785
}
17841786

17851787
/// Alternative representation for `Arg`s describing `self` parameter of methods.

src/libsyntax/mut_visit.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,7 @@ pub fn noop_visit_arg<T: MutVisitor>(Arg { id, pat, ty, source }: &mut Arg, vis:
580580

581581
pub fn noop_visit_arg_source<T: MutVisitor>(source: &mut ArgSource, vis: &mut T) {
582582
match source {
583-
ArgSource::Normal => {},
583+
ArgSource::Normal | ArgSource::Recovery => {},
584584
ArgSource::AsyncFn(pat) => vis.visit_pat(pat),
585585
}
586586
}

src/libsyntax/parse/diagnostics.rs

+20-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::ast;
22
use crate::ast::{
33
BlockCheckMode, BinOpKind, Expr, ExprKind, Item, ItemKind, Pat, PatKind, PathSegment, QSelf,
4-
Ty, TyKind, VariantData,
4+
Ty, TyKind, VariantData, Ident,
55
};
66
use crate::parse::{SeqSep, token, PResult, Parser};
77
use crate::parse::parser::{BlockMode, PathStyle, SemiColonMode, TokenType, TokenExpectType};
@@ -1092,12 +1092,12 @@ impl<'a> Parser<'a> {
10921092
pat: P<ast::Pat>,
10931093
require_name: bool,
10941094
is_trait_item: bool,
1095-
) {
1095+
) -> Option<Ident> {
10961096
// If we find a pattern followed by an identifier, it could be an (incorrect)
10971097
// C-style parameter declaration.
10981098
if self.check_ident() && self.look_ahead(1, |t| {
10991099
*t == token::Comma || *t == token::CloseDelim(token::Paren)
1100-
}) {
1100+
}) { // `fn foo(String s) {}`
11011101
let ident = self.parse_ident().unwrap();
11021102
let span = pat.span.with_hi(ident.span.hi());
11031103

@@ -1107,18 +1107,30 @@ impl<'a> Parser<'a> {
11071107
String::from("<identifier>: <type>"),
11081108
Applicability::HasPlaceholders,
11091109
);
1110-
} else if require_name && is_trait_item {
1111-
if let PatKind::Ident(_, ident, _) = pat.node {
1110+
return Some(ident);
1111+
} else if let PatKind::Ident(_, ident, _) = pat.node {
1112+
if require_name && (
1113+
is_trait_item ||
1114+
self.token == token::Comma ||
1115+
self.token == token::CloseDelim(token::Paren)
1116+
) { // `fn foo(a, b) {}` or `fn foo(usize, usize) {}`
1117+
err.span_suggestion(
1118+
pat.span,
1119+
"if this was a parameter name, give it a type",
1120+
format!("{}: TypeName", ident),
1121+
Applicability::HasPlaceholders,
1122+
);
11121123
err.span_suggestion(
11131124
pat.span,
1114-
"explicitly ignore parameter",
1125+
"if this is a type, explicitly ignore the parameter name",
11151126
format!("_: {}", ident),
11161127
Applicability::MachineApplicable,
11171128
);
1129+
err.note("anonymous parameters are removed in the 2018 edition (see RFC 1685)");
1130+
return Some(ident);
11181131
}
1119-
1120-
err.note("anonymous parameters are removed in the 2018 edition (see RFC 1685)");
11211132
}
1133+
None
11221134
}
11231135

11241136
crate fn recover_arg_parse(&mut self) -> PResult<'a, (P<ast::Pat>, P<ast::Ty>)> {

src/libsyntax/parse/parser.rs

+36-9
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ use crate::parse::diagnostics::Error;
5151

5252
use errors::{Applicability, DiagnosticBuilder, DiagnosticId, FatalError};
5353
use rustc_target::spec::abi::{self, Abi};
54+
use rustc_data_structures::fx::FxHashSet;
5455
use syntax_pos::{Span, BytePos, DUMMY_SP, FileName, hygiene::CompilerDesugaringKind};
5556
use log::debug;
5657

@@ -452,19 +453,18 @@ impl From<P<Expr>> for LhsExpr {
452453
}
453454

454455
/// Creates a placeholder argument.
455-
fn dummy_arg(span: Span) -> Arg {
456-
let ident = Ident::new(kw::Invalid, span);
456+
fn dummy_arg(ident: Ident) -> Arg {
457457
let pat = P(Pat {
458458
id: ast::DUMMY_NODE_ID,
459459
node: PatKind::Ident(BindingMode::ByValue(Mutability::Immutable), ident, None),
460-
span,
460+
span: ident.span,
461461
});
462462
let ty = Ty {
463463
node: TyKind::Err,
464-
span,
464+
span: ident.span,
465465
id: ast::DUMMY_NODE_ID
466466
};
467-
Arg { ty: P(ty), pat: pat, id: ast::DUMMY_NODE_ID, source: ast::ArgSource::Normal }
467+
Arg { ty: P(ty), pat: pat, id: ast::DUMMY_NODE_ID, source: ast::ArgSource::Recovery }
468468
}
469469

470470
#[derive(Copy, Clone, Debug)]
@@ -1528,8 +1528,17 @@ impl<'a> Parser<'a> {
15281528
let pat = self.parse_pat(Some("argument name"))?;
15291529

15301530
if let Err(mut err) = self.expect(&token::Colon) {
1531-
self.argument_without_type(&mut err, pat, require_name, is_trait_item);
1532-
return Err(err);
1531+
if let Some(ident) = self.argument_without_type(
1532+
&mut err,
1533+
pat,
1534+
require_name,
1535+
is_trait_item,
1536+
) {
1537+
err.emit();
1538+
return Ok(dummy_arg(ident));
1539+
} else {
1540+
return Err(err);
1541+
}
15331542
}
15341543

15351544
self.eat_incorrect_doc_comment("a method argument's type");
@@ -5431,7 +5440,7 @@ impl<'a> Parser<'a> {
54315440
p.eat_to_tokens(&[&token::Comma, &token::CloseDelim(token::Paren)]);
54325441
// Create a placeholder argument for proper arg count (issue #34264).
54335442
let span = lo.to(p.prev_span);
5434-
Ok(Some(dummy_arg(span)))
5443+
Ok(Some(dummy_arg(Ident::new(kw::Invalid, span))))
54355444
}
54365445
}
54375446
}
@@ -5584,7 +5593,7 @@ impl<'a> Parser<'a> {
55845593

55855594
// Parse the rest of the function parameter list.
55865595
let sep = SeqSep::trailing_allowed(token::Comma);
5587-
let (fn_inputs, recovered) = if let Some(self_arg) = self_arg {
5596+
let (mut fn_inputs, recovered) = if let Some(self_arg) = self_arg {
55885597
if self.check(&token::CloseDelim(token::Paren)) {
55895598
(vec![self_arg], false)
55905599
} else if self.eat(&token::Comma) {
@@ -5607,6 +5616,24 @@ impl<'a> Parser<'a> {
56075616
// Parse closing paren and return type.
56085617
self.expect(&token::CloseDelim(token::Paren))?;
56095618
}
5619+
// Replace duplicated recovered arguments with `_` pattern to avoid unecessary errors.
5620+
let mut seen_inputs = FxHashSet::default();
5621+
for input in fn_inputs.iter_mut() {
5622+
let opt_ident = if let (PatKind::Ident(_, ident, _), ast::ArgSource::Recovery) = (
5623+
&input.pat.node, &input.source,
5624+
) {
5625+
Some(*ident)
5626+
} else {
5627+
None
5628+
};
5629+
if let Some(ident) = opt_ident {
5630+
if seen_inputs.contains(&ident) {
5631+
input.pat.node = PatKind::Wild;
5632+
}
5633+
seen_inputs.insert(ident);
5634+
}
5635+
}
5636+
56105637
Ok(P(FnDecl {
56115638
inputs: fn_inputs,
56125639
output: self.parse_ret_ty(true)?,

src/test/ui/anon-params-denied-2018.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@ trait T {
66
fn foo(i32); //~ expected one of `:` or `@`, found `)`
77

88
fn bar_with_default_impl(String, String) {}
9-
//~^ ERROR expected one of `:` or `@`, found `,`
9+
//~^ ERROR expected one of `:`
10+
//~| ERROR expected one of `:`
11+
12+
// do not complain about missing `b`
13+
fn baz(a:usize, b, c: usize) -> usize { //~ ERROR expected one of `:`
14+
a + b + c
15+
}
1016
}
1117

1218
fn main() {}

src/test/ui/anon-params-denied-2018.stderr

+51-7
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,65 @@ error: expected one of `:` or `@`, found `)`
22
--> $DIR/anon-params-denied-2018.rs:6:15
33
|
44
LL | fn foo(i32);
5-
| ---^ expected one of `:` or `@` here
6-
| |
7-
| help: explicitly ignore parameter: `_: i32`
5+
| ^ expected one of `:` or `@` here
86
|
97
= note: anonymous parameters are removed in the 2018 edition (see RFC 1685)
8+
help: if this was a parameter name, give it a type
9+
|
10+
LL | fn foo(i32: TypeName);
11+
| ^^^^^^^^^^^^^
12+
help: if this is a type, explicitly ignore the parameter name
13+
|
14+
LL | fn foo(_: i32);
15+
| ^^^^^^
1016

1117
error: expected one of `:` or `@`, found `,`
1218
--> $DIR/anon-params-denied-2018.rs:8:36
1319
|
1420
LL | fn bar_with_default_impl(String, String) {}
15-
| ------^ expected one of `:` or `@` here
16-
| |
17-
| help: explicitly ignore parameter: `_: String`
21+
| ^ expected one of `:` or `@` here
22+
|
23+
= note: anonymous parameters are removed in the 2018 edition (see RFC 1685)
24+
help: if this was a parameter name, give it a type
25+
|
26+
LL | fn bar_with_default_impl(String: TypeName, String) {}
27+
| ^^^^^^^^^^^^^^^^
28+
help: if this is a type, explicitly ignore the parameter name
29+
|
30+
LL | fn bar_with_default_impl(_: String, String) {}
31+
| ^^^^^^^^^
32+
33+
error: expected one of `:` or `@`, found `)`
34+
--> $DIR/anon-params-denied-2018.rs:8:44
35+
|
36+
LL | fn bar_with_default_impl(String, String) {}
37+
| ^ expected one of `:` or `@` here
1838
|
1939
= note: anonymous parameters are removed in the 2018 edition (see RFC 1685)
40+
help: if this was a parameter name, give it a type
41+
|
42+
LL | fn bar_with_default_impl(String, String: TypeName) {}
43+
| ^^^^^^^^^^^^^^^^
44+
help: if this is a type, explicitly ignore the parameter name
45+
|
46+
LL | fn bar_with_default_impl(String, _: String) {}
47+
| ^^^^^^^^^
48+
49+
error: expected one of `:` or `@`, found `,`
50+
--> $DIR/anon-params-denied-2018.rs:13:22
51+
|
52+
LL | fn baz(a:usize, b, c: usize) -> usize {
53+
| ^ expected one of `:` or `@` here
54+
|
55+
= note: anonymous parameters are removed in the 2018 edition (see RFC 1685)
56+
help: if this was a parameter name, give it a type
57+
|
58+
LL | fn baz(a:usize, b: TypeName, c: usize) -> usize {
59+
| ^^^^^^^^^^^
60+
help: if this is a type, explicitly ignore the parameter name
61+
|
62+
LL | fn baz(a:usize, _: b, c: usize) -> usize {
63+
| ^^^^
2064

21-
error: aborting due to 2 previous errors
65+
error: aborting due to 4 previous errors
2266

src/test/ui/parser/inverted-parameters.rs

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ fn pattern((i32, i32) (a, b)) {}
2020

2121
fn fizz(i32) {}
2222
//~^ ERROR expected one of `:` or `@`
23+
//~| HELP if this was a parameter name, give it a type
24+
//~| HELP if this is a type, explicitly ignore the parameter name
2325

2426
fn missing_colon(quux S) {}
2527
//~^ ERROR expected one of `:` or `@`

src/test/ui/parser/inverted-parameters.stderr

+11-1
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,19 @@ error: expected one of `:` or `@`, found `)`
3333
|
3434
LL | fn fizz(i32) {}
3535
| ^ expected one of `:` or `@` here
36+
|
37+
= note: anonymous parameters are removed in the 2018 edition (see RFC 1685)
38+
help: if this was a parameter name, give it a type
39+
|
40+
LL | fn fizz(i32: TypeName) {}
41+
| ^^^^^^^^^^^^^
42+
help: if this is a type, explicitly ignore the parameter name
43+
|
44+
LL | fn fizz(_: i32) {}
45+
| ^^^^^^
3646

3747
error: expected one of `:` or `@`, found `S`
38-
--> $DIR/inverted-parameters.rs:24:23
48+
--> $DIR/inverted-parameters.rs:26:23
3949
|
4050
LL | fn missing_colon(quux S) {}
4151
| -----^

src/test/ui/parser/omitted-arg-in-item-fn.stderr

+10
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@ error: expected one of `:` or `@`, found `)`
33
|
44
LL | fn foo(x) {
55
| ^ expected one of `:` or `@` here
6+
|
7+
= note: anonymous parameters are removed in the 2018 edition (see RFC 1685)
8+
help: if this was a parameter name, give it a type
9+
|
10+
LL | fn foo(x: TypeName) {
11+
| ^^^^^^^^^^^
12+
help: if this is a type, explicitly ignore the parameter name
13+
|
14+
LL | fn foo(_: x) {
15+
| ^^^^
616

717
error: aborting due to previous error
818

src/test/ui/span/issue-34264.stderr

+20
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,32 @@ error: expected one of `:` or `@`, found `)`
99
|
1010
LL | fn foo(Option<i32>, String) {}
1111
| ^ expected one of `:` or `@` here
12+
|
13+
= note: anonymous parameters are removed in the 2018 edition (see RFC 1685)
14+
help: if this was a parameter name, give it a type
15+
|
16+
LL | fn foo(Option<i32>, String: TypeName) {}
17+
| ^^^^^^^^^^^^^^^^
18+
help: if this is a type, explicitly ignore the parameter name
19+
|
20+
LL | fn foo(Option<i32>, _: String) {}
21+
| ^^^^^^^^^
1222

1323
error: expected one of `:` or `@`, found `,`
1424
--> $DIR/issue-34264.rs:3:9
1525
|
1626
LL | fn bar(x, y: usize) {}
1727
| ^ expected one of `:` or `@` here
28+
|
29+
= note: anonymous parameters are removed in the 2018 edition (see RFC 1685)
30+
help: if this was a parameter name, give it a type
31+
|
32+
LL | fn bar(x: TypeName, y: usize) {}
33+
| ^^^^^^^^^^^
34+
help: if this is a type, explicitly ignore the parameter name
35+
|
36+
LL | fn bar(_: x, y: usize) {}
37+
| ^^^^
1838

1939
error[E0061]: this function takes 2 parameters but 3 parameters were supplied
2040
--> $DIR/issue-34264.rs:7:5

0 commit comments

Comments
 (0)