about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc_passes/diagnostics.rs14
-rw-r--r--src/libsyntax/lib.rs1
-rw-r--r--src/libsyntax/parse/parser.rs53
-rw-r--r--src/test/ui/E0642.rs15
-rw-r--r--src/test/ui/E0642.stderr9
5 files changed, 71 insertions, 21 deletions
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<Pat>, P<Ty>)> = 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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`.