From 90a6954327bb4f018eab155f43299d4bf67abe41 Mon Sep 17 00:00:00 2001 From: varkor Date: Sat, 4 Aug 2018 02:23:21 +0100 Subject: [PATCH] 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. --- src/librustc_passes/diagnostics.rs | 14 +++++++- src/libsyntax/lib.rs | 1 + src/libsyntax/parse/parser.rs | 53 +++++++++++++++++++----------- src/test/ui/E0642.rs | 15 +++++++++ src/test/ui/E0642.stderr | 9 +++++ 5 files changed, 71 insertions(+), 21 deletions(-) create mode 100644 src/test/ui/E0642.rs create mode 100644 src/test/ui/E0642.stderr diff --git a/src/librustc_passes/diagnostics.rs b/src/librustc_passes/diagnostics.rs index f1ec3371c3b..b78f2ca676d 100644 --- a/src/librustc_passes/diagnostics.rs +++ b/src/librustc_passes/diagnostics.rs @@ -261,6 +261,19 @@ let result = loop { // ok! ``` "##, +E0642: r##" +Trait methods currently cannot take patterns as arguments. + +Example of erroneous code: + +```compile_fail,E0642 +trait Foo { + fn foo((x, y): (i32, i32)); // error: patterns aren't allowed + // in methods without bodies +} +``` +"##, + E0695: r##" A `break` statement without a label appeared inside a labeled block. @@ -306,7 +319,6 @@ register_diagnostics! { E0561, // patterns aren't allowed in function pointer types E0567, // auto traits can not have generic parameters E0568, // auto traits can not have super traits - E0642, // patterns aren't allowed in methods without bodies E0666, // nested `impl Trait` is illegal E0667, // `impl Trait` in projections E0696, // `continue` pointing to a labeled block diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index c8e60620248..76035a73d1a 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -26,6 +26,7 @@ #![feature(slice_sort_by_cached_key)] #![feature(str_escape)] #![feature(unicode_internals)] +#![feature(catch_expr)] #![recursion_limit="256"] diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 9011b6e48b9..5a2fd5f0145 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1371,7 +1371,7 @@ impl<'a> Parser<'a> { let ident = self.parse_ident()?; let mut generics = self.parse_generics()?; - let d = self.parse_fn_decl_with_self(|p: &mut Parser<'a>|{ + let d = self.parse_fn_decl_with_self(|p: &mut Parser<'a>| { // This is somewhat dubious; We don't want to allow // argument names to be left off if there is a // definition... @@ -1744,30 +1744,43 @@ impl<'a> Parser<'a> { fn parse_arg_general(&mut self, require_name: bool) -> PResult<'a, Arg> { maybe_whole!(self, NtArg, |x| x); - let (pat, ty) = if require_name || self.is_named_argument() { - debug!("parse_arg_general parse_pat (require_name:{})", - require_name); - let pat = self.parse_pat()?; + let parser_snapshot_before_pat = 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 + // better errors to the user. + let pat_arg: PResult<'a, (P, P)> = do catch { + let pat = self.parse_pat()?; self.expect(&token::Colon)?; (pat, self.parse_ty()?) - } else { - 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, - }); - (pat, ty) }; - Ok(Arg { - ty, - pat, - id: ast::DUMMY_NODE_ID, - }) + 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 }) + } + } + } } /// Parse a single function argument diff --git a/src/test/ui/E0642.rs b/src/test/ui/E0642.rs new file mode 100644 index 00000000000..a09846cb3a1 --- /dev/null +++ b/src/test/ui/E0642.rs @@ -0,0 +1,15 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +trait Foo { + fn foo((x, y): (i32, i32)); //~ ERROR patterns aren't allowed in methods without bodies +} + +fn main() {} diff --git a/src/test/ui/E0642.stderr b/src/test/ui/E0642.stderr new file mode 100644 index 00000000000..edc430d578b --- /dev/null +++ b/src/test/ui/E0642.stderr @@ -0,0 +1,9 @@ +error[E0642]: patterns aren't allowed in methods without bodies + --> $DIR/E0642.rs:12:12 + | +LL | fn foo((x, y): (i32, i32)); //~ ERROR patterns aren't allowed in methods without bodies + | ^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0642`.