Auto merge of #35015 - petrochenkov:forearg, r=nikomatsakis

Properly enforce the "patterns aren't allowed in foreign functions" rule

Cases like `arg @ PATTERN` or `mut arg` were missing.
Apply the same rule to function pointer types.

Closes https://github.com/rust-lang/rust/issues/35203
[breaking-change], no breakage in sane code is expected though
r? @nikomatsakis

This is somewhat related to https://github.com/rust-lang/rfcs/pull/1685 (cc @matklad).
The goal is to eventually support full pattern syntax where it makes sense (function body may present) and to support *only* the following forms - `TYPE`, `ident: TYPE`, `_: TYPE` - where patterns don't make sense (function body doesn't present), i.e. in foreign functions and function pointer types.
diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs
index a90b563..d2cf48e 100644
--- a/src/librustc_passes/ast_validation.rs
+++ b/src/librustc_passes/ast_validation.rs
@@ -55,6 +55,17 @@
             err.emit();
         }
     }
+
+    fn check_decl_no_pat<ReportFn: Fn(Span, bool)>(&self, decl: &FnDecl, report_err: ReportFn) {
+        for arg in &decl.inputs {
+            match arg.pat.node {
+                PatKind::Ident(BindingMode::ByValue(Mutability::Immutable), _, None) |
+                PatKind::Wild => {}
+                PatKind::Ident(..) => report_err(arg.pat.span, true),
+                _ => report_err(arg.pat.span, false),
+            }
+        }
+    }
 }
 
 impl<'a> Visitor for AstValidator<'a> {
@@ -82,6 +93,23 @@
         visit::walk_expr(self, expr)
     }
 
+    fn visit_ty(&mut self, ty: &Ty) {
+        match ty.node {
+            TyKind::BareFn(ref bfty) => {
+                self.check_decl_no_pat(&bfty.decl, |span, _| {
+                    let mut err = struct_span_err!(self.session, span, E0561,
+                                            "patterns aren't allowed in function pointer types");
+                    err.span_note(span, "this is a recent error, see \
+                                         issue #35203 for more details");
+                    err.emit();
+                });
+            }
+            _ => {}
+        }
+
+        visit::walk_ty(self, ty)
+    }
+
     fn visit_path(&mut self, path: &Path, id: NodeId) {
         if path.global && path.segments.len() > 0 {
             let ident = path.segments[0].identifier;
@@ -135,6 +163,25 @@
         visit::walk_item(self, item)
     }
 
+    fn visit_foreign_item(&mut self, fi: &ForeignItem) {
+        match fi.node {
+            ForeignItemKind::Fn(ref decl, _) => {
+                self.check_decl_no_pat(decl, |span, is_recent| {
+                    let mut err = struct_span_err!(self.session, span, E0130,
+                                        "patterns aren't allowed in foreign function declarations");
+                    if is_recent {
+                        err.span_note(span, "this is a recent error, see \
+                                             issue #35203 for more details");
+                    }
+                    err.emit();
+                });
+            }
+            ForeignItemKind::Static(..) => {}
+        }
+
+        visit::walk_foreign_item(self, fi)
+    }
+
     fn visit_variant_data(&mut self, vdata: &VariantData, _: Ident,
                           _: &Generics, _: NodeId, span: Span) {
         if vdata.fields().is_empty() {
diff --git a/src/librustc_passes/diagnostics.rs b/src/librustc_passes/diagnostics.rs
index a616b95..3e2dd47 100644
--- a/src/librustc_passes/diagnostics.rs
+++ b/src/librustc_passes/diagnostics.rs
@@ -49,6 +49,39 @@
 ```
 "##,
 
+E0130: r##"
+You declared a pattern as an argument in a foreign function declaration.
+Erroneous code example:
+
+```compile_fail
+extern {
+    fn foo((a, b): (u32, u32)); // error: patterns aren't allowed in foreign
+                                //        function declarations
+}
+```
+
+Please replace the pattern argument with a regular one. Example:
+
+```
+struct SomeStruct {
+    a: u32,
+    b: u32,
+}
+
+extern {
+    fn foo(s: SomeStruct); // ok!
+}
+```
+
+Or:
+
+```
+extern {
+    fn foo(a: (u32, u32)); // ok!
+}
+```
+"##,
+
 E0161: r##"
 A value was moved. However, its size was not known at compile time, and only
 values of a known size can be moved.
@@ -187,4 +220,5 @@
 
 register_diagnostics! {
     E0472, // asm! is unsupported on this target
+    E0561, // patterns aren't allowed in function pointer types
 }
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index ec95afe..4486748 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -60,8 +60,6 @@
 
 use astconv::{AstConv, ast_region_to_region, Bounds, PartitionedBounds, partition_bounds};
 use lint;
-use hir::def::Def;
-use hir::def_id::DefId;
 use constrained_type_params as ctp;
 use middle::lang_items::SizedTraitLangItem;
 use middle::const_val::ConstVal;
@@ -74,7 +72,6 @@
 use rustc::ty::util::IntTypeExt;
 use rscope::*;
 use rustc::dep_graph::DepNode;
-use rustc::hir::map as hir_map;
 use util::common::{ErrorReported, MemoizationMap};
 use util::nodemap::{NodeMap, FnvHashMap};
 use {CrateCtxt, write_ty_to_tcx};
@@ -91,9 +88,9 @@
 use syntax::ptr::P;
 use syntax_pos::Span;
 
-use rustc::hir::{self, PatKind};
-use rustc::hir::intravisit;
-use rustc::hir::print as pprust;
+use rustc::hir::{self, intravisit, map as hir_map, print as pprust};
+use rustc::hir::def::Def;
+use rustc::hir::def_id::DefId;
 
 ///////////////////////////////////////////////////////////////////////////
 // Main entry point
@@ -2144,14 +2141,6 @@
     abi: abi::Abi)
     -> ty::TypeScheme<'tcx>
 {
-    for i in &decl.inputs {
-        match i.pat.node {
-            PatKind::Binding(..) | PatKind::Wild => {}
-            _ => span_err!(ccx.tcx.sess, i.pat.span, E0130,
-                           "patterns aren't allowed in foreign function declarations")
-        }
-    }
-
     let ty_generics = ty_generics_for_fn(ccx, ast_generics, &ty::Generics::empty());
 
     let rb = BindingRscope::new();
diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs
index cd2259a..b655d95 100644
--- a/src/librustc_typeck/diagnostics.rs
+++ b/src/librustc_typeck/diagnostics.rs
@@ -1800,39 +1800,6 @@
 parameter if so.
 "##,
 
-E0130: r##"
-You declared a pattern as an argument in a foreign function declaration.
-Erroneous code example:
-
-```compile_fail
-extern {
-    fn foo((a, b): (u32, u32)); // error: patterns aren't allowed in foreign
-                                //        function declarations
-}
-```
-
-Please replace the pattern argument with a regular one. Example:
-
-```
-struct SomeStruct {
-    a: u32,
-    b: u32,
-}
-
-extern {
-    fn foo(s: SomeStruct); // ok!
-}
-```
-
-Or:
-
-```
-extern {
-    fn foo(a: (u32, u32)); // ok!
-}
-```
-"##,
-
 E0131: r##"
 It is not possible to define `main` with type parameters, or even with function
 parameters. When `main` is present, it must take no arguments and return `()`.
diff --git a/src/test/compile-fail/no-patterns-in-args.rs b/src/test/compile-fail/no-patterns-in-args.rs
new file mode 100644
index 0000000..3edbdf4
--- /dev/null
+++ b/src/test/compile-fail/no-patterns-in-args.rs
@@ -0,0 +1,30 @@
+// Copyright 2016 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.
+
+extern {
+    fn f1(mut arg: u8); //~ ERROR patterns aren't allowed in foreign function declarations
+                        //~^ NOTE this is a recent error
+    fn f2(&arg: u8); //~ ERROR patterns aren't allowed in foreign function declarations
+    fn f3(arg @ _: u8); //~ ERROR patterns aren't allowed in foreign function declarations
+                        //~^ NOTE this is a recent error
+    fn g1(arg: u8); // OK
+    fn g2(_: u8); // OK
+    // fn g3(u8); // Not yet
+}
+
+type A1 = fn(mut arg: u8); //~ ERROR patterns aren't allowed in function pointer types
+                           //~^ NOTE this is a recent error
+type A2 = fn(&arg: u8); //~ ERROR patterns aren't allowed in function pointer types
+                        //~^ NOTE this is a recent error
+type B1 = fn(arg: u8); // OK
+type B2 = fn(_: u8); // OK
+type B3 = fn(u8); // OK
+
+fn main() {}