Skip to content

Commit b15e71a

Browse files
committed
diag: improve handling of patterns in fn ptr and trait decl
1 parent d98a5da commit b15e71a

23 files changed

+244
-103
lines changed

compiler/rustc_ast_passes/messages.ftl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,8 +222,10 @@ ast_passes_out_of_order_params = {$param_ord} parameters must be declared prior
222222
223223
ast_passes_pattern_in_bodiless = patterns aren't allowed in functions without bodies
224224
.label = pattern not allowed in function without body
225+
.suggestion = give this argument a name or use an underscore to ignore it
225226
226227
ast_passes_pattern_in_fn_pointer = patterns aren't allowed in function pointer types
228+
.suggestion = give this argument a name or use an underscore to ignore it
227229
228230
ast_passes_pattern_in_foreign = patterns aren't allowed in foreign function declarations
229231
.label = pattern not allowed in foreign function

compiler/rustc_ast_passes/src/ast_validation.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -788,8 +788,11 @@ impl<'a> AstValidator<'a> {
788788
TyKind::BareFn(bfty) => {
789789
self.check_bare_fn_safety(bfty.decl_span, bfty.safety);
790790
self.check_fn_decl(&bfty.decl, SelfSemantic::No);
791-
Self::check_decl_no_pat(&bfty.decl, |span, _, _| {
792-
self.dcx().emit_err(errors::PatternFnPointer { span });
791+
Self::check_decl_no_pat(&bfty.decl, |span, ident, _| {
792+
// `self` in bare fn ptr is already an error; don't add an extra one.
793+
if !ident.is_some_and(|ident| ident.name == kw::SelfLower) {
794+
self.dcx().emit_err(errors::PatternFnPointer { span });
795+
}
793796
});
794797
if let Extern::Implicit(extern_span) = bfty.ext {
795798
self.handle_missing_abi(extern_span, ty.id);

compiler/rustc_ast_passes/src/errors.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,7 @@ impl Subdiagnostic for EmptyLabelManySpans {
404404
#[diag(ast_passes_pattern_in_fn_pointer, code = E0561)]
405405
pub(crate) struct PatternFnPointer {
406406
#[primary_span]
407+
#[suggestion(code = "_", style = "verbose")]
407408
pub span: Span,
408409
}
409410

@@ -685,6 +686,7 @@ pub(crate) struct PatternInForeign {
685686
pub(crate) struct PatternInBodiless {
686687
#[primary_span]
687688
#[label]
689+
#[suggestion(code = "_", style = "verbose")]
688690
pub span: Span,
689691
}
690692

compiler/rustc_parse/messages.ftl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -753,8 +753,9 @@ parse_path_found_c_variadic_params = `Trait(...)` syntax does not support c_vari
753753
parse_path_found_named_params = `Trait(...)` syntax does not support named parameters
754754
.suggestion = remove the parameter name
755755
756-
parse_pattern_method_param_without_body = patterns aren't allowed in methods without bodies
757-
.suggestion = give this argument a name or use an underscore to ignore it
756+
parse_pattern_in_trait_fn_in_2015 = patterns aren't allowed in trait methods in the 2015 edition
757+
.note = upgrading editions will fix this error
758+
.suggestion = in this edition, give this argument a name or use an underscore to ignore it
758759
759760
parse_pattern_on_wrong_side_of_at = pattern on wrong side of `@`
760761
.label_pattern = pattern on the left, should be on the right

compiler/rustc_parse/src/errors.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1490,10 +1490,11 @@ pub(crate) struct AttributeOnParamType {
14901490
}
14911491

14921492
#[derive(Diagnostic)]
1493-
#[diag(parse_pattern_method_param_without_body, code = E0642)]
1494-
pub(crate) struct PatternMethodParamWithoutBody {
1493+
#[diag(parse_pattern_in_trait_fn_in_2015)]
1494+
#[note]
1495+
pub(crate) struct PatternInTraitFnIn2015 {
14951496
#[primary_span]
1496-
#[suggestion(code = "_", applicability = "machine-applicable", style = "verbose")]
1497+
#[suggestion(code = "_", style = "verbose")]
14971498
pub span: Span,
14981499
}
14991500

compiler/rustc_parse/src/parser/diagnostics.rs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ use crate::errors::{
3838
DocCommentOnParamType, DoubleColonInBound, ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg,
3939
GenericParamsWithoutAngleBrackets, GenericParamsWithoutAngleBracketsSugg,
4040
HelpIdentifierStartsWithNumber, HelpUseLatestEdition, InInTypo, IncorrectAwait,
41-
IncorrectSemicolon, IncorrectUseOfAwait, IncorrectUseOfUse, PatternMethodParamWithoutBody,
41+
IncorrectSemicolon, IncorrectUseOfAwait, IncorrectUseOfUse, PatternInTraitFnIn2015,
4242
QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst, StructLiteralBodyWithoutPath,
4343
StructLiteralBodyWithoutPathSugg, SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma,
4444
TernaryOperator, TernaryOperatorSuggestion, UnexpectedConstInGenericParam,
@@ -2346,16 +2346,28 @@ impl<'a> Parser<'a> {
23462346
None
23472347
}
23482348

2349-
pub(super) fn recover_arg_parse(&mut self) -> PResult<'a, (P<ast::Pat>, P<ast::Ty>)> {
2350-
let pat = self.parse_pat_no_top_alt(Some(Expected::ArgumentName), None)?;
2349+
pub(super) fn recover_arg_parse(
2350+
&mut self,
2351+
emit_error: bool,
2352+
) -> PResult<'a, (P<ast::Pat>, P<ast::Ty>)> {
2353+
let mut pat = self.parse_pat_no_top_alt(Some(Expected::ArgumentName), None)?;
23512354
self.expect(exp!(Colon))?;
23522355
let ty = self.parse_ty()?;
23532356

2354-
self.dcx().emit_err(PatternMethodParamWithoutBody { span: pat.span });
2357+
// Don't emit an error if we can emit a better one during AST passes (i.e., with patterns in
2358+
// bare fn ptrs). This branch is only missed if are guaranteed to emit an error later.
2359+
if emit_error {
2360+
self.dcx().emit_err(PatternInTraitFnIn2015 { span: pat.span });
2361+
2362+
// Pretend the pattern is `_`, to avoid duplicate errors from AST validation.
2363+
pat = P(Pat {
2364+
kind: PatKind::Wild,
2365+
span: pat.span,
2366+
id: ast::DUMMY_NODE_ID,
2367+
tokens: None,
2368+
});
2369+
}
23552370

2356-
// Pretend the pattern is `_`, to avoid duplicate errors from AST validation.
2357-
let pat =
2358-
P(Pat { kind: PatKind::Wild, span: pat.span, id: ast::DUMMY_NODE_ID, tokens: None });
23592371
Ok((pat, ty))
23602372
}
23612373

compiler/rustc_parse/src/parser/item.rs

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2932,6 +2932,8 @@ impl<'a> Parser<'a> {
29322932
/// Parses the parameter list of a function, including the `(` and `)` delimiters.
29332933
pub(super) fn parse_fn_params(&mut self, req_name: ReqName) -> PResult<'a, ThinVec<Param>> {
29342934
let mut first_param = true;
2935+
let is_bare_fn_ptr = self.prev_token.is_keyword(kw::Fn);
2936+
29352937
// Parse the arguments, starting out with `self` being allowed...
29362938
if self.token != TokenKind::OpenParen
29372939
// might be typo'd trait impl, handled elsewhere
@@ -2946,22 +2948,23 @@ impl<'a> Parser<'a> {
29462948
let (mut params, _) = self.parse_paren_comma_seq(|p| {
29472949
p.recover_vcs_conflict_marker();
29482950
let snapshot = p.create_snapshot_for_diagnostic();
2949-
let param = p.parse_param_general(req_name, first_param, true).or_else(|e| {
2950-
let guar = e.emit();
2951-
// When parsing a param failed, we should check to make the span of the param
2952-
// not contain '(' before it.
2953-
// For example when parsing `*mut Self` in function `fn oof(*mut Self)`.
2954-
let lo = if let TokenKind::OpenParen = p.prev_token.kind {
2955-
p.prev_token.span.shrink_to_hi()
2956-
} else {
2957-
p.prev_token.span
2958-
};
2959-
p.restore_snapshot(snapshot);
2960-
// Skip every token until next possible arg or end.
2961-
p.eat_to_tokens(&[exp!(Comma), exp!(CloseParen)]);
2962-
// Create a placeholder argument for proper arg count (issue #34264).
2963-
Ok(dummy_arg(Ident::new(sym::dummy, lo.to(p.prev_token.span)), guar))
2964-
});
2951+
let param =
2952+
p.parse_param_general(req_name, first_param, true, is_bare_fn_ptr).or_else(|e| {
2953+
let guar = e.emit();
2954+
// When parsing a param failed, we should check to make the span of the param
2955+
// not contain '(' before it.
2956+
// For example when parsing `*mut Self` in function `fn oof(*mut Self)`.
2957+
let lo = if let TokenKind::OpenParen = p.prev_token.kind {
2958+
p.prev_token.span.shrink_to_hi()
2959+
} else {
2960+
p.prev_token.span
2961+
};
2962+
p.restore_snapshot(snapshot);
2963+
// Skip every token until next possible arg or end.
2964+
p.eat_to_tokens(&[exp!(Comma), exp!(CloseParen)]);
2965+
// Create a placeholder argument for proper arg count (issue #34264).
2966+
Ok(dummy_arg(Ident::new(sym::dummy, lo.to(p.prev_token.span)), guar))
2967+
});
29652968
// ...now that we've parsed the first argument, `self` is no longer allowed.
29662969
first_param = false;
29672970
param
@@ -2975,11 +2978,13 @@ impl<'a> Parser<'a> {
29752978
///
29762979
/// - `self` is syntactically allowed when `first_param` holds.
29772980
/// - `recover_arg_parse` is used to recover from a failed argument parse.
2981+
/// - `is_bare_fn_ptr` is used to improve diagnostics for bare fn ptrs.
29782982
pub(super) fn parse_param_general(
29792983
&mut self,
29802984
req_name: ReqName,
29812985
first_param: bool,
29822986
recover_arg_parse: bool,
2987+
is_bare_fn_ptr: bool,
29832988
) -> PResult<'a, Param> {
29842989
let lo = self.token.span;
29852990
let attrs = self.parse_outer_attributes()?;
@@ -3040,6 +3045,7 @@ impl<'a> Parser<'a> {
30403045
ty = this.unexpected_any();
30413046
}
30423047
}
3048+
30433049
match ty {
30443050
Ok(ty) => {
30453051
let pat = this.mk_pat(ty.span, PatKind::Missing);
@@ -3052,7 +3058,7 @@ impl<'a> Parser<'a> {
30523058
// Recover from attempting to parse the argument as a type without pattern.
30533059
err.cancel();
30543060
this.restore_snapshot(parser_snapshot_before_ty);
3055-
this.recover_arg_parse()?
3061+
this.recover_arg_parse(!is_bare_fn_ptr)?
30563062
}
30573063
Err(err) => return Err(err),
30583064
}

compiler/rustc_parse/src/parser/path.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,7 @@ impl<'a> Parser<'a> {
400400

401401
let dcx = self.dcx();
402402
let parse_params_result = self.parse_paren_comma_seq(|p| {
403-
let param = p.parse_param_general(|_| false, false, false);
403+
let param = p.parse_param_general(|_| false, false, false, false);
404404
param.map(move |param| {
405405
if !matches!(param.pat.kind, PatKind::Missing) {
406406
dcx.emit_err(FnPathFoundNamedParams {

tests/ui/error-codes/E0642-2015.fixed

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//@ run-rustfix
2+
3+
#![allow(unused)] // for rustfix
4+
5+
#[derive(Clone, Copy)]
6+
struct S;
7+
8+
trait T {
9+
fn foo(_: (i32, i32)); //~ ERROR patterns aren't allowed in trait methods in the 2015 edition
10+
11+
fn bar(_: (i32, i32)) {} //~ ERROR patterns aren't allowed in trait methods in the 2015 edition
12+
fn method(_: S) {} //~ ERROR patterns aren't allowed in trait methods in the 2015 edition
13+
14+
fn f(&ident: &S) {} // ok
15+
fn g(&&ident: &&S) {} // ok
16+
fn h(mut ident: S) {} // ok
17+
}
18+
19+
fn main() {}

tests/ui/error-codes/E0642-2015.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//@ run-rustfix
2+
3+
#![allow(unused)] // for rustfix
4+
5+
#[derive(Clone, Copy)]
6+
struct S;
7+
8+
trait T {
9+
fn foo((x, y): (i32, i32)); //~ ERROR patterns aren't allowed in trait methods in the 2015 edition
10+
11+
fn bar((x, y): (i32, i32)) {} //~ ERROR patterns aren't allowed in trait methods in the 2015 edition
12+
fn method(S { .. }: S) {} //~ ERROR patterns aren't allowed in trait methods in the 2015 edition
13+
14+
fn f(&ident: &S) {} // ok
15+
fn g(&&ident: &&S) {} // ok
16+
fn h(mut ident: S) {} // ok
17+
}
18+
19+
fn main() {}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
error: patterns aren't allowed in trait methods in the 2015 edition
2+
--> $DIR/E0642-2015.rs:9:12
3+
|
4+
LL | fn foo((x, y): (i32, i32));
5+
| ^^^^^^
6+
|
7+
= note: upgrading editions will fix this error
8+
help: in this edition, give this argument a name or use an underscore to ignore it
9+
|
10+
LL - fn foo((x, y): (i32, i32));
11+
LL + fn foo(_: (i32, i32));
12+
|
13+
14+
error: patterns aren't allowed in trait methods in the 2015 edition
15+
--> $DIR/E0642-2015.rs:11:12
16+
|
17+
LL | fn bar((x, y): (i32, i32)) {}
18+
| ^^^^^^
19+
|
20+
= note: upgrading editions will fix this error
21+
help: in this edition, give this argument a name or use an underscore to ignore it
22+
|
23+
LL - fn bar((x, y): (i32, i32)) {}
24+
LL + fn bar(_: (i32, i32)) {}
25+
|
26+
27+
error: patterns aren't allowed in trait methods in the 2015 edition
28+
--> $DIR/E0642-2015.rs:12:15
29+
|
30+
LL | fn method(S { .. }: S) {}
31+
| ^^^^^^^^
32+
|
33+
= note: upgrading editions will fix this error
34+
help: in this edition, give this argument a name or use an underscore to ignore it
35+
|
36+
LL - fn method(S { .. }: S) {}
37+
LL + fn method(_: S) {}
38+
|
39+
40+
error: aborting due to 3 previous errors
41+

tests/ui/error-codes/E0642.fixed

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//@ edition:2024
12
//@ run-rustfix
23

34
#![allow(unused)] // for rustfix
@@ -6,11 +7,10 @@
67
struct S;
78

89
trait T {
9-
fn foo(_: (i32, i32)); //~ ERROR patterns aren't allowed in methods without bodies
10+
fn foo(_: (i32, i32)); //~ ERROR patterns aren't allowed in functions without bodies [E0642]
1011

11-
fn bar(_: (i32, i32)) {} //~ ERROR patterns aren't allowed in methods without bodies
12-
13-
fn method(_: S) {} //~ ERROR patterns aren't allowed in methods without bodies
12+
fn bar((x, y): (i32, i32)) {} // ok
13+
fn method(S { .. }: S) {} // ok
1414

1515
fn f(&ident: &S) {} // ok
1616
fn g(&&ident: &&S) {} // ok

tests/ui/error-codes/E0642.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//@ edition:2024
12
//@ run-rustfix
23

34
#![allow(unused)] // for rustfix
@@ -6,11 +7,10 @@
67
struct S;
78

89
trait T {
9-
fn foo((x, y): (i32, i32)); //~ ERROR patterns aren't allowed in methods without bodies
10+
fn foo((x, y): (i32, i32)); //~ ERROR patterns aren't allowed in functions without bodies [E0642]
1011

11-
fn bar((x, y): (i32, i32)) {} //~ ERROR patterns aren't allowed in methods without bodies
12-
13-
fn method(S { .. }: S) {} //~ ERROR patterns aren't allowed in methods without bodies
12+
fn bar((x, y): (i32, i32)) {} // ok
13+
fn method(S { .. }: S) {} // ok
1414

1515
fn f(&ident: &S) {} // ok
1616
fn g(&&ident: &&S) {} // ok

tests/ui/error-codes/E0642.stderr

Lines changed: 4 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,15 @@
1-
error[E0642]: patterns aren't allowed in methods without bodies
2-
--> $DIR/E0642.rs:9:12
1+
error[E0642]: patterns aren't allowed in functions without bodies
2+
--> $DIR/E0642.rs:10:12
33
|
44
LL | fn foo((x, y): (i32, i32));
5-
| ^^^^^^
5+
| ^^^^^^ pattern not allowed in function without body
66
|
77
help: give this argument a name or use an underscore to ignore it
88
|
99
LL - fn foo((x, y): (i32, i32));
1010
LL + fn foo(_: (i32, i32));
1111
|
1212

13-
error[E0642]: patterns aren't allowed in methods without bodies
14-
--> $DIR/E0642.rs:11:12
15-
|
16-
LL | fn bar((x, y): (i32, i32)) {}
17-
| ^^^^^^
18-
|
19-
help: give this argument a name or use an underscore to ignore it
20-
|
21-
LL - fn bar((x, y): (i32, i32)) {}
22-
LL + fn bar(_: (i32, i32)) {}
23-
|
24-
25-
error[E0642]: patterns aren't allowed in methods without bodies
26-
--> $DIR/E0642.rs:13:15
27-
|
28-
LL | fn method(S { .. }: S) {}
29-
| ^^^^^^^^
30-
|
31-
help: give this argument a name or use an underscore to ignore it
32-
|
33-
LL - fn method(S { .. }: S) {}
34-
LL + fn method(_: S) {}
35-
|
36-
37-
error: aborting due to 3 previous errors
13+
error: aborting due to 1 previous error
3814

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

tests/ui/issues/issue-50571.fixed

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#![allow(dead_code)]
55
trait Foo {
66
fn foo(_: [i32; 2]) {}
7-
//~^ ERROR: patterns aren't allowed in methods without bodies
7+
//~^ ERROR: patterns aren't allowed in trait methods in the 2015 edition
88
}
99

1010
fn main() {}

tests/ui/issues/issue-50571.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#![allow(dead_code)]
55
trait Foo {
66
fn foo([a, b]: [i32; 2]) {}
7-
//~^ ERROR: patterns aren't allowed in methods without bodies
7+
//~^ ERROR: patterns aren't allowed in trait methods in the 2015 edition
88
}
99

1010
fn main() {}

0 commit comments

Comments
 (0)