Skip to content

Commit

Permalink
Fix handling of trait methods with bodies and improve efficiency
Browse files Browse the repository at this point in the history
  • Loading branch information
varkor committed Aug 11, 2018
1 parent b05f0be commit 235905c
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 39 deletions.
26 changes: 14 additions & 12 deletions src/librustc_passes/ast_validation.rs
Expand Up @@ -336,22 +336,24 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
if let TraitItemKind::Method(ref sig, ref block) = trait_item.node {
self.check_trait_fn_not_async(trait_item.span, sig.header.asyncness);
self.check_trait_fn_not_const(sig.header.constness);
if block.is_none() {
self.check_decl_no_pat(&sig.decl, |span, mut_ident| {
if mut_ident {
self.check_decl_no_pat(&sig.decl, |span, mut_ident| {
if mut_ident {
if block.is_none() {
self.session.buffer_lint(
lint::builtin::PATTERNS_IN_FNS_WITHOUT_BODY,
trait_item.id, span,
"patterns aren't allowed in methods without bodies");
} else {
let mut err = struct_span_err!(self.session, span, E0642,
"patterns aren't allowed in methods without bodies");
err.span_suggestion(span,
"use an underscore to ignore the name", "_".to_owned());
err.emit();
"patterns aren't allowed in trait methods");
}
});
}
} else {
let mut err = struct_span_err!(self.session, span, E0642,
"patterns aren't allowed in trait methods");
let suggestion = "give this argument a name or use an \
underscore to ignore it, instead of a \
tuple pattern";
err.span_suggestion(span, suggestion, "_".to_owned());
err.emit();
}
});
}
}
}
Expand Down
49 changes: 30 additions & 19 deletions src/libsyntax/parse/parser.rs
Expand Up @@ -1744,7 +1744,16 @@ impl<'a> Parser<'a> {
fn parse_arg_general(&mut self, require_name: bool) -> PResult<'a, Arg> {
maybe_whole!(self, NtArg, |x| x);

let parser_snapshot_before_pat = self.clone();
// If we see `ident :`, then we know that the argument is just of the
// form `type`, which means we won't need to recover from parsing a
// pattern and so we don't need to store a parser snapshot.
let parser_snapshot_before_pat = if
self.look_ahead(1, |t| t.is_ident()) &&
self.look_ahead(2, |t| t == &token::Colon) {
None
} else {
Some(self.clone())
};

// We're going to try parsing the argument as a pattern (even if it's not
// allowed, such as for trait methods without bodies). This way we can provide
Expand All @@ -1755,29 +1764,31 @@ impl<'a> Parser<'a> {
(pat, self.parse_ty()?)
};

let is_named_argument = self.is_named_argument();
match pat_arg {
Ok((pat, ty)) => {
Ok(Arg { ty, pat, id: ast::DUMMY_NODE_ID })
}
Err(mut err) => {
if require_name || is_named_argument {
Err(err)
} else {
err.cancel();
// Recover from attempting to parse the argument as a pattern. This means
// the type is alone, with no name, e.g. `fn foo(u32)`.
mem::replace(self, parser_snapshot_before_pat);
debug!("parse_arg_general ident_to_pat");
let ident = Ident::new(keywords::Invalid.name(), self.prev_span);
let ty = self.parse_ty()?;
let pat = P(Pat {
id: ast::DUMMY_NODE_ID,
node: PatKind::Ident(
BindingMode::ByValue(Mutability::Immutable), ident, None),
span: ty.span,
});
Ok(Arg { ty, pat, id: ast::DUMMY_NODE_ID })
match (require_name || self.is_named_argument(), parser_snapshot_before_pat) {
(true, _) | (_, None) => {
Err(err)
}
(false, Some(parser_snapshot_before_pat)) => {
err.cancel();
// Recover from attempting to parse the argument as a pattern. This means
// the type is alone, with no name, e.g. `fn foo(u32)`.
mem::replace(self, parser_snapshot_before_pat);
debug!("parse_arg_general ident_to_pat");
let ident = Ident::new(keywords::Invalid.name(), self.prev_span);
let ty = self.parse_ty()?;
let pat = P(Pat {
id: ast::DUMMY_NODE_ID,
node: PatKind::Ident(
BindingMode::ByValue(Mutability::Immutable), ident, None),
span: ty.span,
});
Ok(Arg { ty, pat, id: ast::DUMMY_NODE_ID })
}
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/test/compile-fail/no-patterns-in-args-2.rs
Expand Up @@ -11,9 +11,9 @@
#![deny(patterns_in_fns_without_body)]

trait Tr {
fn f1(mut arg: u8); //~ ERROR patterns aren't allowed in methods without bodies
fn f1(mut arg: u8); //~ ERROR patterns aren't allowed in trait methods
//~^ WARN was previously accepted
fn f2(&arg: u8); //~ ERROR patterns aren't allowed in methods without bodies
fn f2(&arg: u8); //~ ERROR patterns aren't allowed in trait methods
fn g1(arg: u8); // OK
fn g2(_: u8); // OK
#[allow(anonymous_parameters)]
Expand Down
2 changes: 1 addition & 1 deletion src/test/compile-fail/no-patterns-in-args-macro.rs
Expand Up @@ -30,7 +30,7 @@ mod bad_pat {
m!((bad, pat));
//~^ ERROR patterns aren't allowed in function pointer types
//~| ERROR patterns aren't allowed in foreign function declarations
//~| ERROR patterns aren't allowed in methods without bodies
//~| ERROR patterns aren't allowed in trait methods
}

fn main() {}
6 changes: 5 additions & 1 deletion src/test/ui/E0642.rs
Expand Up @@ -9,7 +9,11 @@
// except according to those terms.

trait Foo {
fn foo((x, y): (i32, i32)); //~ ERROR patterns aren't allowed in methods without bodies
fn foo((x, y): (i32, i32)); //~ ERROR patterns aren't allowed in trait methods
}

trait Bar {
fn bar((x, y): (i32, i32)) {} //~ ERROR patterns aren't allowed in trait methods
}

fn main() {}
22 changes: 18 additions & 4 deletions src/test/ui/E0642.stderr
@@ -1,9 +1,23 @@
error[E0642]: patterns aren't allowed in methods without bodies
error[E0642]: patterns aren't allowed in trait methods
--> $DIR/E0642.rs:12:12
|
LL | fn foo((x, y): (i32, i32)); //~ ERROR patterns aren't allowed in methods without bodies
| ^^^^^^ help: use an underscore to ignore the name: `_`
LL | fn foo((x, y): (i32, i32)); //~ ERROR patterns aren't allowed in trait methods
| ^^^^^^
help: give this argument a name or use an underscore to ignore it, instead of a tuple pattern
|
LL | fn foo(_: (i32, i32)); //~ ERROR patterns aren't allowed in trait methods
| ^

error[E0642]: patterns aren't allowed in trait methods
--> $DIR/E0642.rs:16:12
|
LL | fn bar((x, y): (i32, i32)) {} //~ ERROR patterns aren't allowed in trait methods
| ^^^^^^
help: give this argument a name or use an underscore to ignore it, instead of a tuple pattern
|
LL | fn bar(_: (i32, i32)) {} //~ ERROR patterns aren't allowed in trait methods
| ^

error: aborting due to previous error
error: aborting due to 2 previous errors

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

0 comments on commit 235905c

Please sign in to comment.