Skip to content

Commit 6dd9bcc

Browse files
committed
Rollup merge of rust-lang#53051 - varkor:trait-method-pattern-arguments-error, r=petrochenkov
Emit error for pattern arguments in trait methods The error and check for this already existed, but the parser didn't try to parse trait method arguments as patterns, so the error was never emitted. This surfaces the error, so we get better errors than simple parse errors. This improves the error message described in rust-lang#53046.
2 parents bc0c0ba + 5c814e2 commit 6dd9bcc

File tree

5 files changed

+122
-16
lines changed

5 files changed

+122
-16
lines changed

src/librustc_passes/diagnostics.rs

+21-1
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,27 @@ let result = loop { // ok!
261261
```
262262
"##,
263263

264+
E0642: r##"
265+
Trait methods currently cannot take patterns as arguments.
266+
267+
Example of erroneous code:
268+
269+
```compile_fail,E0642
270+
trait Foo {
271+
fn foo((x, y): (i32, i32)); // error: patterns aren't allowed
272+
// in trait methods
273+
}
274+
```
275+
276+
You can instead use a single name for the argument:
277+
278+
```
279+
trait Foo {
280+
fn foo(x_and_y: (i32, i32)); // ok!
281+
}
282+
```
283+
"##,
284+
264285
E0695: r##"
265286
A `break` statement without a label appeared inside a labeled block.
266287
@@ -306,7 +327,6 @@ register_diagnostics! {
306327
E0561, // patterns aren't allowed in function pointer types
307328
E0567, // auto traits can not have generic parameters
308329
E0568, // auto traits can not have super traits
309-
E0642, // patterns aren't allowed in methods without bodies
310330
E0666, // nested `impl Trait` is illegal
311331
E0667, // `impl Trait` in projections
312332
E0696, // `continue` pointing to a labeled block

src/libsyntax/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#![feature(slice_sort_by_cached_key)]
2828
#![feature(str_escape)]
2929
#![feature(unicode_internals)]
30+
#![feature(catch_expr)]
3031

3132
#![recursion_limit="256"]
3233

src/libsyntax/parse/parser.rs

+53-15
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ use ast::{RangeEnd, RangeSyntax};
4444
use {ast, attr};
4545
use codemap::{self, CodeMap, Spanned, respan};
4646
use syntax_pos::{self, Span, MultiSpan, BytePos, FileName, edition::Edition};
47-
use errors::{self, Applicability, DiagnosticBuilder};
47+
use errors::{self, Applicability, DiagnosticBuilder, DiagnosticId};
4848
use parse::{self, SeqSep, classify, token};
4949
use parse::lexer::TokenAndSpan;
5050
use parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration};
@@ -1371,7 +1371,7 @@ impl<'a> Parser<'a> {
13711371
let ident = self.parse_ident()?;
13721372
let mut generics = self.parse_generics()?;
13731373

1374-
let d = self.parse_fn_decl_with_self(|p: &mut Parser<'a>|{
1374+
let d = self.parse_fn_decl_with_self(|p: &mut Parser<'a>| {
13751375
// This is somewhat dubious; We don't want to allow
13761376
// argument names to be left off if there is a
13771377
// definition...
@@ -1753,21 +1753,59 @@ impl<'a> Parser<'a> {
17531753
(pat, self.parse_ty()?)
17541754
} else {
17551755
debug!("parse_arg_general ident_to_pat");
1756-
let ident = Ident::new(keywords::Invalid.name(), self.prev_span);
1757-
let ty = self.parse_ty()?;
1758-
let pat = P(Pat {
1759-
id: ast::DUMMY_NODE_ID,
1760-
node: PatKind::Ident(BindingMode::ByValue(Mutability::Immutable), ident, None),
1761-
span: ty.span,
1762-
});
1763-
(pat, ty)
1756+
1757+
let parser_snapshot_before_pat = self.clone();
1758+
1759+
// We're going to try parsing the argument as a pattern (even though it's not
1760+
// allowed). This way we can provide better errors to the user.
1761+
let pat_arg: PResult<'a, _> = do catch {
1762+
let pat = self.parse_pat()?;
1763+
self.expect(&token::Colon)?;
1764+
(pat, self.parse_ty()?)
1765+
};
1766+
1767+
match pat_arg {
1768+
Ok((pat, ty)) => {
1769+
let mut err = self.diagnostic().struct_span_err_with_code(
1770+
pat.span,
1771+
"patterns aren't allowed in methods without bodies",
1772+
DiagnosticId::Error("E0642".into()),
1773+
);
1774+
err.span_suggestion_short_with_applicability(
1775+
pat.span,
1776+
"give this argument a name or use an underscore to ignore it",
1777+
"_".to_owned(),
1778+
Applicability::MachineApplicable,
1779+
);
1780+
err.emit();
1781+
// Pretend the pattern is `_`, to avoid duplicate errors from AST validation.
1782+
let pat = P(Pat {
1783+
node: PatKind::Wild,
1784+
span: pat.span,
1785+
id: ast::DUMMY_NODE_ID
1786+
});
1787+
(pat, ty)
1788+
}
1789+
Err(mut err) => {
1790+
err.cancel();
1791+
// Recover from attempting to parse the argument as a pattern. This means
1792+
// the type is alone, with no name, e.g. `fn foo(u32)`.
1793+
mem::replace(self, parser_snapshot_before_pat);
1794+
debug!("parse_arg_general ident_to_pat");
1795+
let ident = Ident::new(keywords::Invalid.name(), self.prev_span);
1796+
let ty = self.parse_ty()?;
1797+
let pat = P(Pat {
1798+
id: ast::DUMMY_NODE_ID,
1799+
node: PatKind::Ident(
1800+
BindingMode::ByValue(Mutability::Immutable), ident, None),
1801+
span: ty.span,
1802+
});
1803+
(pat, ty)
1804+
}
1805+
}
17641806
};
17651807

1766-
Ok(Arg {
1767-
ty,
1768-
pat,
1769-
id: ast::DUMMY_NODE_ID,
1770-
})
1808+
Ok(Arg { ty, pat, id: ast::DUMMY_NODE_ID })
17711809
}
17721810

17731811
/// Parse a single function argument

src/test/ui/E0642.rs

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#[derive(Clone, Copy)]
12+
struct S;
13+
14+
trait T {
15+
fn foo((x, y): (i32, i32)); //~ ERROR patterns aren't allowed in methods without bodies
16+
17+
fn bar((x, y): (i32, i32)) {} //~ ERROR patterns aren't allowed in methods without bodies
18+
19+
fn f(&ident: &S) {} // ok
20+
fn g(&&ident: &&S) {} // ok
21+
fn h(mut ident: S) {} // ok
22+
}
23+
24+
fn main() {}

src/test/ui/E0642.stderr

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error[E0642]: patterns aren't allowed in methods without bodies
2+
--> $DIR/E0642.rs:15:12
3+
|
4+
LL | fn foo((x, y): (i32, i32)); //~ ERROR patterns aren't allowed in methods without bodies
5+
| ^^^^^^
6+
help: give this argument a name or use an underscore to ignore it
7+
|
8+
LL | fn foo(_: (i32, i32)); //~ ERROR patterns aren't allowed in methods without bodies
9+
| ^
10+
11+
error[E0642]: patterns aren't allowed in methods without bodies
12+
--> $DIR/E0642.rs:17:12
13+
|
14+
LL | fn bar((x, y): (i32, i32)) {} //~ ERROR patterns aren't allowed in methods without bodies
15+
| ^^^^^^
16+
help: give this argument a name or use an underscore to ignore it
17+
|
18+
LL | fn bar(_: (i32, i32)) {} //~ ERROR patterns aren't allowed in methods without bodies
19+
| ^
20+
21+
error: aborting due to 2 previous errors
22+
23+
For more information about this error, try `rustc --explain E0642`.

0 commit comments

Comments
 (0)