about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc_data_structures/thin_vec.rs6
-rw-r--r--src/librustc_passes/ast_validation.rs45
-rw-r--r--src/libsyntax/ast.rs4
-rw-r--r--src/libsyntax/attr/mod.rs2
-rw-r--r--src/libsyntax/config.rs9
-rw-r--r--src/libsyntax/ext/build.rs5
-rw-r--r--src/libsyntax/ext/expand.rs5
-rw-r--r--src/libsyntax/feature_gate.rs15
-rw-r--r--src/libsyntax/mut_visit.rs3
-rw-r--r--src/libsyntax/parse/attr.rs11
-rw-r--r--src/libsyntax/parse/diagnostics.rs8
-rw-r--r--src/libsyntax/parse/lexer/mod.rs1
-rw-r--r--src/libsyntax/parse/mod.rs2
-rw-r--r--src/libsyntax/parse/parser.rs80
-rw-r--r--src/libsyntax/visit.rs3
-rw-r--r--src/libsyntax_ext/deriving/generic/mod.rs2
-rw-r--r--src/libsyntax_pos/symbol.rs1
-rw-r--r--src/test/ui/parser/fn-arg-doc-comment.rs18
-rw-r--r--src/test/ui/parser/fn-arg-doc-comment.stderr46
-rw-r--r--src/test/ui/rfc-2565-param-attrs/param-attrs-2018.rs8
-rw-r--r--src/test/ui/rfc-2565-param-attrs/param-attrs-2018.stderr18
-rw-r--r--src/test/ui/rfc-2565-param-attrs/param-attrs-allowed.rs225
-rw-r--r--src/test/ui/rfc-2565-param-attrs/param-attrs-builtin-attrs.rs145
-rw-r--r--src/test/ui/rfc-2565-param-attrs/param-attrs-builtin-attrs.stderr339
-rw-r--r--src/test/ui/rfc-2565-param-attrs/param-attrs-cfg.rs79
-rw-r--r--src/test/ui/rfc-2565-param-attrs/param-attrs-cfg.stderr68
-rw-r--r--src/test/ui/rfc-2565-param-attrs/param-attrs-feature-gate.rs14
-rw-r--r--src/test/ui/rfc-2565-param-attrs/param-attrs-feature-gate.stderr27
28 files changed, 1115 insertions, 74 deletions
diff --git a/src/librustc_data_structures/thin_vec.rs b/src/librustc_data_structures/thin_vec.rs
index 52f23f4893e..6692903cd4f 100644
--- a/src/librustc_data_structures/thin_vec.rs
+++ b/src/librustc_data_structures/thin_vec.rs
@@ -66,3 +66,9 @@ impl<T: HashStable<CTX>, CTX> HashStable<CTX> for ThinVec<T> {
         (**self).hash_stable(hcx, hasher)
     }
 }
+
+impl<T> Default for ThinVec<T> {
+    fn default() -> Self {
+        Self(None)
+    }
+}
diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs
index 532cec2af15..00b6db0ed9a 100644
--- a/src/librustc_passes/ast_validation.rs
+++ b/src/librustc_passes/ast_validation.rs
@@ -14,6 +14,7 @@ use rustc::session::Session;
 use rustc_data_structures::fx::FxHashMap;
 use syntax::ast::*;
 use syntax::attr;
+use syntax::feature_gate::is_builtin_attr;
 use syntax::source_map::Spanned;
 use syntax::symbol::{kw, sym};
 use syntax::ptr::P;
@@ -365,6 +366,29 @@ impl<'a> AstValidator<'a> {
             _ => None,
         }
     }
+
+    fn check_fn_decl(&self, fn_decl: &FnDecl) {
+        fn_decl
+            .inputs
+            .iter()
+            .flat_map(|i| i.attrs.as_ref())
+            .filter(|attr| {
+                let arr = [sym::allow, sym::cfg, sym::cfg_attr, sym::deny, sym::forbid, sym::warn];
+                !arr.contains(&attr.name_or_empty()) && is_builtin_attr(attr)
+            })
+            .for_each(|attr| if attr.is_sugared_doc {
+                let mut err = self.err_handler().struct_span_err(
+                    attr.span,
+                    "documentation comments cannot be applied to function parameters"
+                );
+                err.span_label(attr.span, "doc comments are not allowed here");
+                err.emit();
+            }
+            else {
+                self.err_handler().span_err(attr.span, "allow, cfg, cfg_attr, deny, \
+                forbid, and warn are the only allowed built-in attributes in function parameters")
+            });
+    }
 }
 
 enum GenericPosition {
@@ -470,6 +494,9 @@ fn validate_generics_order<'a>(
 impl<'a> Visitor<'a> for AstValidator<'a> {
     fn visit_expr(&mut self, expr: &'a Expr) {
         match expr.node {
+            ExprKind::Closure(_, _, _, ref fn_decl, _, _) => {
+                self.check_fn_decl(fn_decl);
+            }
             ExprKind::IfLet(_, ref expr, _, _) | ExprKind::WhileLet(_, ref expr, _, _) =>
                 self.while_if_let_ambiguity(&expr),
             ExprKind::InlineAsm(..) if !self.session.target.target.options.allow_asm => {
@@ -484,6 +511,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
     fn visit_ty(&mut self, ty: &'a Ty) {
         match ty.node {
             TyKind::BareFn(ref bfty) => {
+                self.check_fn_decl(&bfty.decl);
                 self.check_decl_no_pat(&bfty.decl, |span, _| {
                     struct_span_err!(self.session, span, E0561,
                                      "patterns aren't allowed in function pointer types").emit();
@@ -601,10 +629,11 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                         .note("only trait implementations may be annotated with default").emit();
                 }
             }
-            ItemKind::Fn(_, ref header, ref generics, _) => {
+            ItemKind::Fn(ref decl, ref header, ref generics, _) => {
+                self.visit_fn_header(header);
+                self.check_fn_decl(decl);
                 // We currently do not permit const generics in `const fn`, as
                 // this is tantamount to allowing compile-time dependent typing.
-                self.visit_fn_header(header);
                 if header.constness.node == Constness::Const {
                     // Look for const generics and error if we find any.
                     for param in &generics.params {
@@ -657,6 +686,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 self.no_questions_in_bounds(bounds, "supertraits", true);
                 for trait_item in trait_items {
                     if let TraitItemKind::Method(ref sig, ref block) = trait_item.node {
+                        self.check_fn_decl(&sig.decl);
                         self.check_trait_fn_not_async(trait_item.span, sig.header.asyncness.node);
                         self.check_trait_fn_not_const(sig.header.constness);
                         if block.is_none() {
@@ -711,6 +741,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
     fn visit_foreign_item(&mut self, fi: &'a ForeignItem) {
         match fi.node {
             ForeignItemKind::Fn(ref decl, _) => {
+                self.check_fn_decl(decl);
                 self.check_decl_no_pat(decl, |span, _| {
                     struct_span_err!(self.session, span, E0130,
                                      "patterns aren't allowed in foreign function declarations")
@@ -864,6 +895,16 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                              "`async fn` is not permitted in the 2015 edition").emit();
         }
     }
+
+    fn visit_impl_item(&mut self, ii: &'a ImplItem) {
+        match ii.node {
+            ImplItemKind::Method(ref sig, _) => {
+                self.check_fn_decl(&sig.decl);
+            }
+            _ => {}
+        }
+        visit::walk_impl_item(self, ii);
+    }
 }
 
 pub fn check_crate(session: &Session, krate: &Crate) -> (bool, bool) {
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 02fbcb14fa5..68cb8c8574d 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -1770,6 +1770,7 @@ pub struct InlineAsm {
 /// E.g., `bar: usize` as in `fn foo(bar: usize)`.
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
 pub struct Arg {
+    pub attrs: ThinVec<Attribute>,
     pub ty: P<Ty>,
     pub pat: P<Pat>,
     pub id: NodeId,
@@ -1817,7 +1818,7 @@ impl Arg {
         }
     }
 
-    pub fn from_self(eself: ExplicitSelf, eself_ident: Ident) -> Arg {
+    pub fn from_self(attrs: ThinVec<Attribute>, eself: ExplicitSelf, eself_ident: Ident) -> Arg {
         let span = eself.span.to(eself_ident.span);
         let infer_ty = P(Ty {
             id: DUMMY_NODE_ID,
@@ -1825,6 +1826,7 @@ impl Arg {
             span,
         });
         let arg = |mutbl, ty| Arg {
+            attrs,
             pat: P(Pat {
                 id: DUMMY_NODE_ID,
                 node: PatKind::Ident(BindingMode::ByValue(mutbl), eself_ident, None),
diff --git a/src/libsyntax/attr/mod.rs b/src/libsyntax/attr/mod.rs
index b5d9b761773..f99397408ba 100644
--- a/src/libsyntax/attr/mod.rs
+++ b/src/libsyntax/attr/mod.rs
@@ -724,7 +724,7 @@ macro_rules! derive_has_attrs {
 
 derive_has_attrs! {
     Item, Expr, Local, ast::ForeignItem, ast::StructField, ast::ImplItem, ast::TraitItem, ast::Arm,
-    ast::Field, ast::FieldPat, ast::Variant_
+    ast::Field, ast::FieldPat, ast::Variant_, ast::Arg
 }
 
 pub fn inject(mut krate: ast::Crate, parse_sess: &ParseSess, attrs: &[String]) -> ast::Crate {
diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs
index 6123e95ccf8..1cc13ac7878 100644
--- a/src/libsyntax/config.rs
+++ b/src/libsyntax/config.rs
@@ -298,6 +298,10 @@ impl<'a> StripUnconfigured<'a> {
         }
     }
 
+    pub fn configure_fn_decl(&mut self, fn_decl: &mut ast::FnDecl) {
+        fn_decl.inputs.flat_map_in_place(|arg| self.configure(arg));
+    }
+
     /// Denies `#[cfg]` on generic parameters until we decide what to do with it.
     /// See issue #51279.
     pub fn disallow_cfg_on_generic_param(&mut self, param: &ast::GenericParam) {
@@ -364,6 +368,11 @@ impl<'a> MutVisitor for StripUnconfigured<'a> {
         self.configure_pat(pat);
         noop_visit_pat(pat, self)
     }
+
+    fn visit_fn_decl(&mut self, mut fn_decl: &mut P<ast::FnDecl>) {
+        self.configure_fn_decl(&mut fn_decl);
+        noop_visit_fn_decl(fn_decl, self);
+    }
 }
 
 fn is_cfg(attr: &ast::Attribute) -> bool {
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs
index 2a03e49996b..9d4bf7d518d 100644
--- a/src/libsyntax/ext/build.rs
+++ b/src/libsyntax/ext/build.rs
@@ -963,9 +963,10 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
     fn arg(&self, span: Span, ident: ast::Ident, ty: P<ast::Ty>) -> ast::Arg {
         let arg_pat = self.pat_ident(span, ident);
         ast::Arg {
-            ty,
+            attrs: ThinVec::default(),
+            id: ast::DUMMY_NODE_ID,
             pat: arg_pat,
-            id: ast::DUMMY_NODE_ID
+            ty,
         }
     }
 
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 99605395553..671c01c53bb 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -1616,6 +1616,11 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
             *id = self.cx.resolver.next_node_id()
         }
     }
+
+    fn visit_fn_decl(&mut self, mut fn_decl: &mut P<ast::FnDecl>) {
+        self.cfg.configure_fn_decl(&mut fn_decl);
+        noop_visit_fn_decl(fn_decl, self);
+    }
 }
 
 pub struct ExpansionConfig<'feat> {
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index b41b91c7631..93683074a57 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -560,6 +560,9 @@ declare_features! (
     // Allows the user of associated type bounds.
     (active, associated_type_bounds, "1.34.0", Some(52662), None),
 
+    // Attributes on formal function params
+    (active, param_attrs, "1.36.0", Some(60406), None),
+
     // Allows calling constructor functions in `const fn`
     // FIXME Create issue
     (active, const_constructor, "1.37.0", Some(61456), None),
@@ -2508,6 +2511,18 @@ pub fn check_crate(krate: &ast::Crate,
         parse_sess: sess,
         plugin_attributes,
     };
+
+    sess
+        .param_attr_spans
+        .borrow()
+        .iter()
+        .for_each(|span| gate_feature!(
+            &ctx,
+            param_attrs,
+            *span,
+            "attributes on function parameters are unstable"
+        ));
+
     let visitor = &mut PostExpansionVisitor {
         context: &ctx,
         builtin_attributes: &*BUILTIN_ATTRIBUTE_MAP,
diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs
index 2889f8edfc6..02e2c96868d 100644
--- a/src/libsyntax/mut_visit.rs
+++ b/src/libsyntax/mut_visit.rs
@@ -568,8 +568,9 @@ pub fn noop_visit_meta_item<T: MutVisitor>(mi: &mut MetaItem, vis: &mut T) {
     vis.visit_span(span);
 }
 
-pub fn noop_visit_arg<T: MutVisitor>(Arg { id, pat, ty }: &mut Arg, vis: &mut T) {
+pub fn noop_visit_arg<T: MutVisitor>(Arg { attrs, id, pat, ty }: &mut Arg, vis: &mut T) {
     vis.visit_id(id);
+    visit_thin_attrs(attrs, vis);
     vis.visit_pat(pat);
     vis.visit_ty(ty);
 }
diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs
index 77a87e26e60..b28d48b9445 100644
--- a/src/libsyntax/parse/attr.rs
+++ b/src/libsyntax/parse/attr.rs
@@ -18,6 +18,14 @@ const DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG: &str = "an inner attribute is not \
                                                      permitted in this context";
 
 impl<'a> Parser<'a> {
+    crate fn parse_arg_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
+        let attrs = self.parse_outer_attributes()?;
+        attrs.iter().for_each(|a|
+            self.sess.param_attr_spans.borrow_mut().push(a.span)
+        );
+        Ok(attrs)
+    }
+
     /// Parse attributes that appear before an item
     crate fn parse_outer_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
         let mut attrs: Vec<ast::Attribute> = Vec::new();
@@ -35,7 +43,8 @@ impl<'a> Parser<'a> {
                     };
                     let inner_parse_policy =
                         InnerAttributeParsePolicy::NotPermitted { reason: inner_error_reason };
-                    attrs.push(self.parse_attribute_with_inner_parse_policy(inner_parse_policy)?);
+                    let attr = self.parse_attribute_with_inner_parse_policy(inner_parse_policy)?;
+                    attrs.push(attr);
                     just_parsed_doc_comment = false;
                 }
                 token::DocComment(s) => {
diff --git a/src/libsyntax/parse/diagnostics.rs b/src/libsyntax/parse/diagnostics.rs
index c4db9a9df45..472cbcd0f79 100644
--- a/src/libsyntax/parse/diagnostics.rs
+++ b/src/libsyntax/parse/diagnostics.rs
@@ -28,7 +28,7 @@ crate fn dummy_arg(ident: Ident) -> Arg {
         span: ident.span,
         id: ast::DUMMY_NODE_ID
     };
-    Arg { ty: P(ty), pat: pat, id: ast::DUMMY_NODE_ID }
+    Arg { attrs: ThinVec::default(), id: ast::DUMMY_NODE_ID, pat, ty: P(ty) }
 }
 
 pub enum Error {
@@ -1074,11 +1074,11 @@ impl<'a> Parser<'a> {
         Err(err)
     }
 
-    crate fn eat_incorrect_doc_comment(&mut self, applied_to: &str) {
+    crate fn eat_incorrect_doc_comment_for_arg_type(&mut self) {
         if let token::DocComment(_) = self.token.kind {
             let mut err = self.diagnostic().struct_span_err(
                 self.token.span,
-                &format!("documentation comments cannot be applied to {}", applied_to),
+                "documentation comments cannot be applied to a function parameter's type",
             );
             err.span_label(self.token.span, "doc comments are not allowed here");
             err.emit();
@@ -1095,7 +1095,7 @@ impl<'a> Parser<'a> {
             self.bump();
             let mut err = self.diagnostic().struct_span_err(
                 sp,
-                &format!("attributes cannot be applied to {}", applied_to),
+                "attributes cannot be applied to a function parameter's type",
             );
             err.span_label(sp, "attributes are not allowed here");
             err.emit();
diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs
index e3d959c2c54..fca0bda2a3e 100644
--- a/src/libsyntax/parse/lexer/mod.rs
+++ b/src/libsyntax/parse/lexer/mod.rs
@@ -1532,6 +1532,7 @@ mod tests {
             buffered_lints: Lock::new(vec![]),
             edition: Edition::from_session(),
             ambiguous_block_expr_parse: Lock::new(FxHashMap::default()),
+            param_attr_spans: Lock::new(Vec::new()),
         }
     }
 
diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs
index 1d708d39a13..c4da876a2e7 100644
--- a/src/libsyntax/parse/mod.rs
+++ b/src/libsyntax/parse/mod.rs
@@ -54,6 +54,7 @@ pub struct ParseSess {
     /// operation token that followed it, but that the parser cannot identify without further
     /// analysis.
     pub ambiguous_block_expr_parse: Lock<FxHashMap<Span, Span>>,
+    pub param_attr_spans: Lock<Vec<Span>>
 }
 
 impl ParseSess {
@@ -79,6 +80,7 @@ impl ParseSess {
             buffered_lints: Lock::new(vec![]),
             edition: Edition::from_session(),
             ambiguous_block_expr_parse: Lock::new(FxHashMap::default()),
+            param_attr_spans: Lock::new(Vec::new()),
         }
     }
 
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 3acd7088145..7b99f7bd4e5 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -1188,7 +1188,8 @@ impl<'a> Parser<'a> {
                 // definition...
 
                 // We don't allow argument names to be left off in edition 2018.
-                p.parse_arg_general(p.token.span.rust_2018(), true, false)
+                let is_name_required = p.token.span.rust_2018();
+                p.parse_arg_general(true, false, |_| is_name_required)
             })?;
             generics.where_clause = self.parse_where_clause()?;
 
@@ -1487,26 +1488,31 @@ impl<'a> Parser<'a> {
     /// Skips unexpected attributes and doc comments in this position and emits an appropriate
     /// error.
     /// This version of parse arg doesn't necessarily require identifier names.
-    fn parse_arg_general(
+    fn parse_arg_general<F>(
         &mut self,
-        require_name: bool,
         is_trait_item: bool,
         allow_c_variadic: bool,
-    ) -> PResult<'a, Arg> {
-        if let Ok(Some(arg)) = self.parse_self_arg() {
+        is_name_required: F,
+    ) -> PResult<'a, Arg>
+    where
+        F: Fn(&token::Token) -> bool
+    {
+        let attrs = self.parse_arg_attributes()?;
+        if let Ok(Some(mut arg)) = self.parse_self_arg() {
+            arg.attrs = attrs.into();
             return self.recover_bad_self_arg(arg, is_trait_item);
         }
 
-        let (pat, ty) = if require_name || self.is_named_argument() {
-            debug!("parse_arg_general parse_pat (require_name:{})", require_name);
-            self.eat_incorrect_doc_comment("method arguments");
-            let pat = self.parse_pat(Some("argument name"))?;
+        let is_name_required = is_name_required(&self.token);
+        let (pat, ty) = if is_name_required || self.is_named_argument() {
+            debug!("parse_arg_general parse_pat (is_name_required:{})", is_name_required);
 
+            let pat = self.parse_pat(Some("argument name"))?;
             if let Err(mut err) = self.expect(&token::Colon) {
                 if let Some(ident) = self.argument_without_type(
                     &mut err,
                     pat,
-                    require_name,
+                    is_name_required,
                     is_trait_item,
                 ) {
                     err.emit();
@@ -1516,12 +1522,12 @@ impl<'a> Parser<'a> {
                 }
             }
 
-            self.eat_incorrect_doc_comment("a method argument's type");
+            self.eat_incorrect_doc_comment_for_arg_type();
             (pat, self.parse_ty_common(true, true, allow_c_variadic)?)
         } else {
             debug!("parse_arg_general ident_to_pat");
             let parser_snapshot_before_ty = self.clone();
-            self.eat_incorrect_doc_comment("a method argument's type");
+            self.eat_incorrect_doc_comment_for_arg_type();
             let mut ty = self.parse_ty_common(true, true, allow_c_variadic);
             if ty.is_ok() && self.token != token::Comma &&
                self.token != token::CloseDelim(token::Paren) {
@@ -1554,11 +1560,12 @@ impl<'a> Parser<'a> {
             }
         };
 
-        Ok(Arg { ty, pat, id: ast::DUMMY_NODE_ID })
+        Ok(Arg { attrs: attrs.into(), id: ast::DUMMY_NODE_ID, pat, ty })
     }
 
     /// Parses an argument in a lambda header (e.g., `|arg, arg|`).
     fn parse_fn_block_arg(&mut self) -> PResult<'a, Arg> {
+        let attrs = self.parse_arg_attributes()?;
         let pat = self.parse_pat(Some("argument name"))?;
         let t = if self.eat(&token::Colon) {
             self.parse_ty()?
@@ -1570,6 +1577,7 @@ impl<'a> Parser<'a> {
             })
         };
         Ok(Arg {
+            attrs: attrs.into(),
             ty: t,
             pat,
             id: ast::DUMMY_NODE_ID
@@ -5411,15 +5419,19 @@ impl<'a> Parser<'a> {
                 &token::CloseDelim(token::Paren),
                 SeqSep::trailing_allowed(token::Comma),
                 |p| {
-                    // If the argument is a C-variadic argument we should not
-                    // enforce named arguments.
-                    let enforce_named_args = if p.token == token::DotDotDot {
-                        false
-                    } else {
-                        named_args
-                    };
-                    match p.parse_arg_general(enforce_named_args, false,
-                                              allow_c_variadic) {
+                    let do_not_enforce_named_arguments_for_c_variadic =
+                        |token: &token::Token| -> bool {
+                            if token == &token::DotDotDot {
+                                false
+                            } else {
+                                named_args
+                            }
+                        };
+                    match p.parse_arg_general(
+                        false,
+                        allow_c_variadic,
+                        do_not_enforce_named_arguments_for_c_variadic
+                    ) {
                         Ok(arg) => {
                             if let TyKind::CVarArgs = arg.ty.node {
                                 c_variadic = true;
@@ -5464,7 +5476,6 @@ impl<'a> Parser<'a> {
 
     /// Parses the argument list and result type of a function declaration.
     fn parse_fn_decl(&mut self, allow_c_variadic: bool) -> PResult<'a, P<FnDecl>> {
-
         let (args, c_variadic) = self.parse_fn_args(true, allow_c_variadic)?;
         let ret_ty = self.parse_ret_ty(true)?;
 
@@ -5476,6 +5487,8 @@ impl<'a> Parser<'a> {
     }
 
     /// Returns the parsed optional self argument and whether a self shortcut was used.
+    ///
+    /// See `parse_self_arg_with_attrs` to collect attributes.
     fn parse_self_arg(&mut self) -> PResult<'a, Option<Arg>> {
         let expect_ident = |this: &mut Self| match this.token.kind {
             // Preserve hygienic context.
@@ -5581,7 +5594,18 @@ impl<'a> Parser<'a> {
         };
 
         let eself = source_map::respan(eself_lo.to(eself_hi), eself);
-        Ok(Some(Arg::from_self(eself, eself_ident)))
+        Ok(Some(Arg::from_self(ThinVec::default(), eself, eself_ident)))
+    }
+
+    /// Returns the parsed optional self argument with attributes and whether a self
+    /// shortcut was used.
+    fn parse_self_arg_with_attrs(&mut self) -> PResult<'a, Option<Arg>> {
+        let attrs = self.parse_arg_attributes()?;
+        let arg_opt = self.parse_self_arg()?;
+        Ok(arg_opt.map(|mut arg| {
+            arg.attrs = attrs.into();
+            arg
+        }))
     }
 
     /// Parses the parameter list and result type of a function that may have a `self` parameter.
@@ -5591,7 +5615,7 @@ impl<'a> Parser<'a> {
         self.expect(&token::OpenDelim(token::Paren))?;
 
         // Parse optional self argument.
-        let self_arg = self.parse_self_arg()?;
+        let self_arg = self.parse_self_arg_with_attrs()?;
 
         // Parse the rest of the function parameter list.
         let sep = SeqSep::trailing_allowed(token::Comma);
@@ -5865,7 +5889,7 @@ impl<'a> Parser<'a> {
             let ident = self.parse_ident()?;
             let mut generics = self.parse_generics()?;
             let decl = self.parse_fn_decl_with_self(|p| {
-                p.parse_arg_general(true, true, false)
+                p.parse_arg_general(true, false, |_| true)
             })?;
             generics.where_clause = self.parse_where_clause()?;
             *at_end = true;
@@ -7441,7 +7465,7 @@ impl<'a> Parser<'a> {
             } else if self.look_ahead(1, |t| *t == token::OpenDelim(token::Paren)) {
                 let ident = self.parse_ident().unwrap();
                 self.bump();  // `(`
-                let kw_name = if let Ok(Some(_)) = self.parse_self_arg() {
+                let kw_name = if let Ok(Some(_)) = self.parse_self_arg_with_attrs() {
                     "method"
                 } else {
                     "function"
@@ -7492,7 +7516,7 @@ impl<'a> Parser<'a> {
                 self.eat_to_tokens(&[&token::Gt]);
                 self.bump();  // `>`
                 let (kw, kw_name, ambiguous) = if self.eat(&token::OpenDelim(token::Paren)) {
-                    if let Ok(Some(_)) = self.parse_self_arg() {
+                    if let Ok(Some(_)) = self.parse_self_arg_with_attrs() {
                         ("fn", "method", false)
                     } else {
                         ("fn", "function", false)
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 24b0c372471..8132024416a 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -550,8 +550,9 @@ pub fn walk_fn_ret_ty<'a, V: Visitor<'a>>(visitor: &mut V, ret_ty: &'a FunctionR
 
 pub fn walk_fn_decl<'a, V: Visitor<'a>>(visitor: &mut V, function_declaration: &'a FnDecl) {
     for argument in &function_declaration.inputs {
+        walk_list!(visitor, visit_attribute, argument.attrs.iter());
         visitor.visit_pat(&argument.pat);
-        visitor.visit_ty(&argument.ty)
+        visitor.visit_ty(&argument.ty);
     }
     visitor.visit_fn_ret_ty(&function_declaration.output)
 }
diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs
index ffec667aba5..8b719b5477c 100644
--- a/src/libsyntax_ext/deriving/generic/mod.rs
+++ b/src/libsyntax_ext/deriving/generic/mod.rs
@@ -928,7 +928,7 @@ impl<'a> MethodDef<'a> {
         let args = {
             let self_args = explicit_self.map(|explicit_self| {
                 let ident = Ident::with_empty_ctxt(kw::SelfLower).with_span_pos(trait_.span);
-                ast::Arg::from_self(explicit_self, ident)
+                ast::Arg::from_self(ThinVec::default(), explicit_self, ident)
             });
             let nonself_args = arg_types.into_iter()
                 .map(|(name, ty)| cx.arg(trait_.span, name, ty));
diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs
index 49123e4cc30..260bad6dc08 100644
--- a/src/libsyntax_pos/symbol.rs
+++ b/src/libsyntax_pos/symbol.rs
@@ -444,6 +444,7 @@ symbols! {
         panic_implementation,
         panic_runtime,
         partial_cmp,
+        param_attrs,
         PartialOrd,
         passes,
         pat,
diff --git a/src/test/ui/parser/fn-arg-doc-comment.rs b/src/test/ui/parser/fn-arg-doc-comment.rs
index 22af94b6284..4a4f959e213 100644
--- a/src/test/ui/parser/fn-arg-doc-comment.rs
+++ b/src/test/ui/parser/fn-arg-doc-comment.rs
@@ -1,20 +1,20 @@
 pub fn f(
     /// Comment
-    //~^ ERROR documentation comments cannot be applied to method arguments
+    //~^ ERROR documentation comments cannot be applied to function parameters
     //~| NOTE doc comments are not allowed here
+    //~| ERROR attributes on function parameters are unstable
+    //~| NOTE https://github.com/rust-lang/rust/issues/60406
     id: u8,
     /// Other
-    //~^ ERROR documentation comments cannot be applied to method arguments
+    //~^ ERROR documentation comments cannot be applied to function parameters
     //~| NOTE doc comments are not allowed here
+    //~| ERROR attributes on function parameters are unstable
+    //~| NOTE https://github.com/rust-lang/rust/issues/60406
     a: u8,
 ) {}
 
-fn foo(#[allow(dead_code)] id: i32) {}
-//~^ ERROR attributes cannot be applied to method arguments
-//~| NOTE attributes are not allowed here
-
 fn bar(id: #[allow(dead_code)] i32) {}
-//~^ ERROR attributes cannot be applied to a method argument's type
+//~^ ERROR attributes cannot be applied to a function parameter's type
 //~| NOTE attributes are not allowed here
 
 fn main() {
@@ -26,10 +26,6 @@ fn main() {
     //~| ERROR mismatched types
     //~| NOTE expected u8, found reference
     //~| NOTE expected
-    foo("");
-    //~^ ERROR mismatched types
-    //~| NOTE expected i32, found reference
-    //~| NOTE expected
     bar("");
     //~^ ERROR mismatched types
     //~| NOTE expected i32, found reference
diff --git a/src/test/ui/parser/fn-arg-doc-comment.stderr b/src/test/ui/parser/fn-arg-doc-comment.stderr
index 73a24eebb3f..9058e88d1d7 100644
--- a/src/test/ui/parser/fn-arg-doc-comment.stderr
+++ b/src/test/ui/parser/fn-arg-doc-comment.stderr
@@ -1,26 +1,38 @@
-error: documentation comments cannot be applied to method arguments
+error: attributes cannot be applied to a function parameter's type
+  --> $DIR/fn-arg-doc-comment.rs:16:12
+   |
+LL | fn bar(id: #[allow(dead_code)] i32) {}
+   |            ^^^^^^^^^^^^^^^^^^^ attributes are not allowed here
+
+error: documentation comments cannot be applied to function parameters
   --> $DIR/fn-arg-doc-comment.rs:2:5
    |
 LL |     /// Comment
    |     ^^^^^^^^^^^ doc comments are not allowed here
 
-error: documentation comments cannot be applied to method arguments
-  --> $DIR/fn-arg-doc-comment.rs:6:5
+error: documentation comments cannot be applied to function parameters
+  --> $DIR/fn-arg-doc-comment.rs:8:5
    |
 LL |     /// Other
    |     ^^^^^^^^^ doc comments are not allowed here
 
-error: attributes cannot be applied to method arguments
-  --> $DIR/fn-arg-doc-comment.rs:12:8
+error[E0658]: attributes on function parameters are unstable
+  --> $DIR/fn-arg-doc-comment.rs:2:5
+   |
+LL |     /// Comment
+   |     ^^^^^^^^^^^
    |
-LL | fn foo(#[allow(dead_code)] id: i32) {}
-   |        ^^^^^^^^^^^^^^^^^^^ attributes are not allowed here
+   = note: for more information, see https://github.com/rust-lang/rust/issues/60406
+   = help: add #![feature(param_attrs)] to the crate attributes to enable
 
-error: attributes cannot be applied to a method argument's type
-  --> $DIR/fn-arg-doc-comment.rs:16:12
+error[E0658]: attributes on function parameters are unstable
+  --> $DIR/fn-arg-doc-comment.rs:8:5
    |
-LL | fn bar(id: #[allow(dead_code)] i32) {}
-   |            ^^^^^^^^^^^^^^^^^^^ attributes are not allowed here
+LL |     /// Other
+   |     ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/60406
+   = help: add #![feature(param_attrs)] to the crate attributes to enable
 
 error[E0308]: mismatched types
   --> $DIR/fn-arg-doc-comment.rs:22:7
@@ -43,15 +55,6 @@ LL |     f("", "");
 error[E0308]: mismatched types
   --> $DIR/fn-arg-doc-comment.rs:29:9
    |
-LL |     foo("");
-   |         ^^ expected i32, found reference
-   |
-   = note: expected type `i32`
-              found type `&'static str`
-
-error[E0308]: mismatched types
-  --> $DIR/fn-arg-doc-comment.rs:33:9
-   |
 LL |     bar("");
    |         ^^ expected i32, found reference
    |
@@ -60,4 +63,5 @@ LL |     bar("");
 
 error: aborting due to 8 previous errors
 
-For more information about this error, try `rustc --explain E0308`.
+Some errors have detailed explanations: E0308, E0658.
+For more information about an error, try `rustc --explain E0308`.
diff --git a/src/test/ui/rfc-2565-param-attrs/param-attrs-2018.rs b/src/test/ui/rfc-2565-param-attrs/param-attrs-2018.rs
new file mode 100644
index 00000000000..e900ccab4fd
--- /dev/null
+++ b/src/test/ui/rfc-2565-param-attrs/param-attrs-2018.rs
@@ -0,0 +1,8 @@
+// edition:2018
+
+#![feature(param_attrs)]
+
+trait Trait2015 { fn foo(#[allow(C)] i32); }
+//~^ ERROR expected one of `:` or `@`, found `)`
+
+fn main() {}
diff --git a/src/test/ui/rfc-2565-param-attrs/param-attrs-2018.stderr b/src/test/ui/rfc-2565-param-attrs/param-attrs-2018.stderr
new file mode 100644
index 00000000000..d0ed65f2880
--- /dev/null
+++ b/src/test/ui/rfc-2565-param-attrs/param-attrs-2018.stderr
@@ -0,0 +1,18 @@
+error: expected one of `:` or `@`, found `)`
+  --> $DIR/param-attrs-2018.rs:5:41
+   |
+LL | trait Trait2015 { fn foo(#[allow(C)] i32); }
+   |                                         ^ expected one of `:` or `@` here
+   |
+   = note: anonymous parameters are removed in the 2018 edition (see RFC 1685)
+help: if this was a parameter name, give it a type
+   |
+LL | trait Trait2015 { fn foo(#[allow(C)] i32: TypeName); }
+   |                                      ^^^^^^^^^^^^^
+help: if this is a type, explicitly ignore the parameter name
+   |
+LL | trait Trait2015 { fn foo(#[allow(C)] _: i32); }
+   |                                      ^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/rfc-2565-param-attrs/param-attrs-allowed.rs b/src/test/ui/rfc-2565-param-attrs/param-attrs-allowed.rs
new file mode 100644
index 00000000000..c521d04fda5
--- /dev/null
+++ b/src/test/ui/rfc-2565-param-attrs/param-attrs-allowed.rs
@@ -0,0 +1,225 @@
+// compile-flags: --cfg something
+// compile-pass
+
+#![feature(param_attrs)]
+
+extern "C" {
+    fn ffi(
+        #[allow(C)] a: i32,
+        #[cfg(something)] b: i32,
+        #[cfg_attr(something, cfg(nothing))] c: i32,
+        #[deny(C)] d: i32,
+        #[forbid(C)] #[warn(C)] ...
+    );
+}
+
+type FnType = fn(
+    #[allow(C)] a: i32,
+    #[cfg(something)] b: i32,
+    #[cfg_attr(something, cfg(nothing))] c: i32,
+    #[deny(C)] d: i32,
+    #[forbid(C)] #[warn(C)] e: i32
+);
+
+pub fn foo(
+    #[allow(C)] a: i32,
+    #[cfg(something)] b: i32,
+    #[cfg_attr(something, cfg(nothing))] c: i32,
+    #[deny(C)] d: i32,
+    #[forbid(C)] #[warn(C)] e: i32
+) {}
+
+// self, &self and &mut self
+
+struct SelfStruct {}
+impl SelfStruct {
+    fn foo(
+        #[allow(C)] self,
+        #[cfg(something)] a: i32,
+        #[cfg_attr(something, cfg(nothing))]
+        #[deny(C)] b: i32,
+    ) {}
+}
+
+struct RefStruct {}
+impl RefStruct {
+    fn foo(
+        #[allow(C)] &self,
+        #[cfg(something)] a: i32,
+        #[cfg_attr(something, cfg(nothing))]
+        #[deny(C)] b: i32,
+    ) {}
+}
+trait RefTrait {
+    fn foo(
+        #[forbid(C)] &self,
+        #[warn(C)] a: i32
+    ) {}
+}
+impl RefTrait for RefStruct {
+    fn foo(
+        #[forbid(C)] &self,
+        #[warn(C)] a: i32
+    ) {}
+}
+
+struct MutStruct {}
+impl MutStruct {
+    fn foo(
+        #[allow(C)] &mut self,
+        #[cfg(something)] a: i32,
+        #[cfg_attr(something, cfg(nothing))]
+        #[deny(C)] b: i32,
+    ) {}
+}
+trait MutTrait {
+    fn foo(
+        #[forbid(C)] &mut self,
+        #[warn(C)] a: i32
+    ) {}
+}
+impl MutTrait for MutStruct {
+    fn foo(
+        #[forbid(C)] &mut self,
+        #[warn(C)] a: i32
+    ) {}
+}
+
+// self: Self, self: &Self and self: &mut Self
+
+struct NamedSelfSelfStruct {}
+impl NamedSelfSelfStruct {
+    fn foo(
+        #[allow(C)] self: Self,
+        #[cfg(something)] a: i32,
+        #[cfg_attr(something, cfg(nothing))]
+        #[deny(C)] b: i32,
+    ) {}
+}
+
+struct NamedSelfRefStruct {}
+impl NamedSelfRefStruct {
+    fn foo(
+        #[allow(C)] self: &Self,
+        #[cfg(something)] a: i32,
+        #[cfg_attr(something, cfg(nothing))]
+        #[deny(C)] b: i32,
+    ) {}
+}
+trait NamedSelfRefTrait {
+    fn foo(
+        #[forbid(C)] self: &Self,
+        #[warn(C)] a: i32
+    ) {}
+}
+impl NamedSelfRefTrait for NamedSelfRefStruct {
+    fn foo(
+        #[forbid(C)] self: &Self,
+        #[warn(C)] a: i32
+    ) {}
+}
+
+struct NamedSelfMutStruct {}
+impl NamedSelfMutStruct {
+    fn foo(
+        #[allow(C)] self: &mut Self,
+        #[cfg(something)] a: i32,
+        #[cfg_attr(something, cfg(nothing))]
+        #[deny(C)] b: i32,
+    ) {}
+}
+trait NamedSelfMutTrait {
+    fn foo(
+        #[forbid(C)] self: &mut Self,
+        #[warn(C)] a: i32
+    ) {}
+}
+impl NamedSelfMutTrait for NamedSelfMutStruct {
+    fn foo(
+        #[forbid(C)] self: &mut Self,
+        #[warn(C)] a: i32
+    ) {}
+}
+
+// &'a self and &'a mut self
+
+struct NamedLifetimeRefStruct {}
+impl NamedLifetimeRefStruct {
+    fn foo<'a>(
+        #[allow(C)] self: &'a Self,
+        #[cfg(something)] a: i32,
+        #[cfg_attr(something, cfg(nothing))]
+        #[deny(C)] b: i32,
+    ) {}
+}
+trait NamedLifetimeRefTrait {
+    fn foo<'a>(
+        #[forbid(C)] &'a self,
+        #[warn(C)] a: i32
+    ) {}
+}
+impl NamedLifetimeRefTrait for NamedLifetimeRefStruct {
+    fn foo<'a>(
+        #[forbid(C)] &'a self,
+        #[warn(C)] a: i32
+    ) {}
+}
+
+struct NamedLifetimeMutStruct {}
+impl NamedLifetimeMutStruct {
+    fn foo<'a>(
+        #[allow(C)] self: &'a mut Self,
+        #[cfg(something)] a: i32,
+        #[cfg_attr(something, cfg(nothing))]
+        #[deny(C)] b: i32,
+    ) {}
+}
+trait NamedLifetimeMutTrait {
+    fn foo<'a>(
+        #[forbid(C)] &'a mut self,
+        #[warn(C)] a: i32
+    ) {}
+}
+impl NamedLifetimeMutTrait for NamedLifetimeMutStruct {
+    fn foo<'a>(
+        #[forbid(C)] &'a mut self,
+        #[warn(C)] a: i32
+    ) {}
+}
+
+// Box<Self>
+
+struct BoxSelfStruct {}
+impl BoxSelfStruct {
+    fn foo(
+        #[allow(C)] self: Box<Self>,
+        #[cfg(something)] a: i32,
+        #[cfg_attr(something, cfg(nothing))]
+        #[deny(C)] b: i32,
+    ) {}
+}
+trait BoxSelfTrait {
+    fn foo(
+        #[forbid(C)] self: Box<Self>,
+        #[warn(C)] a: i32
+    ) {}
+}
+impl BoxSelfTrait for BoxSelfStruct {
+    fn foo(
+        #[forbid(C)] self: Box<Self>,
+        #[warn(C)] a: i32
+    ) {}
+}
+
+fn main() {
+    let _: unsafe extern "C" fn(_, _, _, ...) = ffi;
+    let _: fn(_, _, _, _) = foo;
+    let _: FnType = |_, _, _, _| {};
+    let c = |
+        #[allow(C)] a: u32,
+        #[cfg(something)] b: i32,
+        #[cfg_attr(something, cfg(nothing))]
+        #[deny(C)] c: i32,
+    | {};
+    let _ = c(1, 2);
+}
diff --git a/src/test/ui/rfc-2565-param-attrs/param-attrs-builtin-attrs.rs b/src/test/ui/rfc-2565-param-attrs/param-attrs-builtin-attrs.rs
new file mode 100644
index 00000000000..352375729bd
--- /dev/null
+++ b/src/test/ui/rfc-2565-param-attrs/param-attrs-builtin-attrs.rs
@@ -0,0 +1,145 @@
+#![feature(param_attrs)]
+
+extern "C" {
+    fn ffi(
+        /// Foo
+        //~^ ERROR documentation comments cannot be applied to function
+        #[test] a: i32,
+        //~^ ERROR The attribute `test` is currently unknown to the compiler and may have
+        /// Bar
+        //~^ ERROR documentation comments cannot be applied to function
+        #[must_use]
+        //~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
+        /// Baz
+        //~^ ERROR documentation comments cannot be applied to function
+        #[no_mangle] b: i32,
+        //~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
+    );
+}
+
+type FnType = fn(
+    /// Foo
+    //~^ ERROR documentation comments cannot be applied to function
+    #[test] a: u32,
+    //~^ ERROR The attribute `test` is currently unknown to the compiler and may have
+    /// Bar
+    //~^ ERROR documentation comments cannot be applied to function
+    #[must_use]
+    //~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
+    /// Baz
+    //~^ ERROR documentation comments cannot be applied to function
+    #[no_mangle] b: i32,
+    //~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
+);
+
+pub fn foo(
+    /// Foo
+    //~^ ERROR documentation comments cannot be applied to function
+    #[test] a: u32,
+    //~^ ERROR The attribute `test` is currently unknown to the compiler and may have
+    /// Bar
+    //~^ ERROR documentation comments cannot be applied to function
+    #[must_use]
+    //~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
+    /// Baz
+    //~^ ERROR documentation comments cannot be applied to function
+    #[no_mangle] b: i32,
+    //~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
+) {}
+
+struct SelfStruct {}
+impl SelfStruct {
+    fn foo(
+        /// Foo
+        //~^ ERROR documentation comments cannot be applied to function
+        self,
+        /// Bar
+        //~^ ERROR documentation comments cannot be applied to function
+        #[test] a: i32,
+        //~^ ERROR The attribute `test` is currently unknown to the compiler and may have
+        /// Baz
+        //~^ ERROR documentation comments cannot be applied to function
+        #[must_use]
+        //~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
+        /// Qux
+        //~^ ERROR documentation comments cannot be applied to function
+        #[no_mangle] b: i32,
+        //~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
+    ) {}
+}
+
+struct RefStruct {}
+impl RefStruct {
+    fn foo(
+        /// Foo
+        //~^ ERROR documentation comments cannot be applied to function
+        &self,
+        /// Bar
+        //~^ ERROR documentation comments cannot be applied to function
+        #[test] a: i32,
+        //~^ ERROR The attribute `test` is currently unknown to the compiler and may have
+        /// Baz
+        //~^ ERROR documentation comments cannot be applied to function
+        #[must_use]
+        //~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
+        /// Qux
+        //~^ ERROR documentation comments cannot be applied to function
+        #[no_mangle] b: i32,
+        //~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
+    ) {}
+}
+trait RefTrait {
+    fn foo(
+        /// Foo
+        //~^ ERROR documentation comments cannot be applied to function
+        &self,
+        /// Bar
+        //~^ ERROR documentation comments cannot be applied to function
+        #[test] a: i32,
+        //~^ ERROR The attribute `test` is currently unknown to the compiler and may have
+        /// Baz
+        //~^ ERROR documentation comments cannot be applied to function
+        #[must_use]
+        //~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
+        /// Qux
+        //~^ ERROR documentation comments cannot be applied to function
+        #[no_mangle] b: i32,
+        //~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
+    ) {}
+}
+impl RefTrait for RefStruct {
+    fn foo(
+        /// Foo
+        //~^ ERROR documentation comments cannot be applied to function
+        &self,
+        /// Bar
+        //~^ ERROR documentation comments cannot be applied to function
+        #[test] a: i32,
+        //~^ ERROR The attribute `test` is currently unknown to the compiler and may have
+        /// Baz
+        //~^ ERROR documentation comments cannot be applied to function
+        #[must_use]
+        //~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
+        /// Qux
+        //~^ ERROR documentation comments cannot be applied to function
+        #[no_mangle] b: i32,
+        //~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
+    ) {}
+}
+
+fn main() {
+    let _ = |
+        /// Foo
+        //~^ ERROR documentation comments cannot be applied to function
+        #[test] a: u32,
+        //~^ ERROR The attribute `test` is currently unknown to the compiler and may have
+        /// Bar
+        //~^ ERROR documentation comments cannot be applied to function
+        #[must_use]
+        //~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
+        /// Baz
+        //~^ ERROR documentation comments cannot be applied to function
+        #[no_mangle] b: i32
+        //~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
+    | {};
+}
diff --git a/src/test/ui/rfc-2565-param-attrs/param-attrs-builtin-attrs.stderr b/src/test/ui/rfc-2565-param-attrs/param-attrs-builtin-attrs.stderr
new file mode 100644
index 00000000000..e6f3efc04ce
--- /dev/null
+++ b/src/test/ui/rfc-2565-param-attrs/param-attrs-builtin-attrs.stderr
@@ -0,0 +1,339 @@
+error: documentation comments cannot be applied to function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:5:9
+   |
+LL |         /// Foo
+   |         ^^^^^^^ doc comments are not allowed here
+
+error: documentation comments cannot be applied to function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:9:9
+   |
+LL |         /// Bar
+   |         ^^^^^^^ doc comments are not allowed here
+
+error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:11:9
+   |
+LL |         #[must_use]
+   |         ^^^^^^^^^^^
+
+error: documentation comments cannot be applied to function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:13:9
+   |
+LL |         /// Baz
+   |         ^^^^^^^ doc comments are not allowed here
+
+error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:15:9
+   |
+LL |         #[no_mangle] b: i32,
+   |         ^^^^^^^^^^^^
+
+error: documentation comments cannot be applied to function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:21:5
+   |
+LL |     /// Foo
+   |     ^^^^^^^ doc comments are not allowed here
+
+error: documentation comments cannot be applied to function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:25:5
+   |
+LL |     /// Bar
+   |     ^^^^^^^ doc comments are not allowed here
+
+error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:27:5
+   |
+LL |     #[must_use]
+   |     ^^^^^^^^^^^
+
+error: documentation comments cannot be applied to function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:29:5
+   |
+LL |     /// Baz
+   |     ^^^^^^^ doc comments are not allowed here
+
+error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:31:5
+   |
+LL |     #[no_mangle] b: i32,
+   |     ^^^^^^^^^^^^
+
+error: documentation comments cannot be applied to function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:36:5
+   |
+LL |     /// Foo
+   |     ^^^^^^^ doc comments are not allowed here
+
+error: documentation comments cannot be applied to function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:40:5
+   |
+LL |     /// Bar
+   |     ^^^^^^^ doc comments are not allowed here
+
+error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:42:5
+   |
+LL |     #[must_use]
+   |     ^^^^^^^^^^^
+
+error: documentation comments cannot be applied to function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:44:5
+   |
+LL |     /// Baz
+   |     ^^^^^^^ doc comments are not allowed here
+
+error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:46:5
+   |
+LL |     #[no_mangle] b: i32,
+   |     ^^^^^^^^^^^^
+
+error: documentation comments cannot be applied to function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:53:9
+   |
+LL |         /// Foo
+   |         ^^^^^^^ doc comments are not allowed here
+
+error: documentation comments cannot be applied to function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:56:9
+   |
+LL |         /// Bar
+   |         ^^^^^^^ doc comments are not allowed here
+
+error: documentation comments cannot be applied to function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:60:9
+   |
+LL |         /// Baz
+   |         ^^^^^^^ doc comments are not allowed here
+
+error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:62:9
+   |
+LL |         #[must_use]
+   |         ^^^^^^^^^^^
+
+error: documentation comments cannot be applied to function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:64:9
+   |
+LL |         /// Qux
+   |         ^^^^^^^ doc comments are not allowed here
+
+error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:66:9
+   |
+LL |         #[no_mangle] b: i32,
+   |         ^^^^^^^^^^^^
+
+error: documentation comments cannot be applied to function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:74:9
+   |
+LL |         /// Foo
+   |         ^^^^^^^ doc comments are not allowed here
+
+error: documentation comments cannot be applied to function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:77:9
+   |
+LL |         /// Bar
+   |         ^^^^^^^ doc comments are not allowed here
+
+error: documentation comments cannot be applied to function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:81:9
+   |
+LL |         /// Baz
+   |         ^^^^^^^ doc comments are not allowed here
+
+error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:83:9
+   |
+LL |         #[must_use]
+   |         ^^^^^^^^^^^
+
+error: documentation comments cannot be applied to function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:85:9
+   |
+LL |         /// Qux
+   |         ^^^^^^^ doc comments are not allowed here
+
+error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:87:9
+   |
+LL |         #[no_mangle] b: i32,
+   |         ^^^^^^^^^^^^
+
+error: documentation comments cannot be applied to function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:93:9
+   |
+LL |         /// Foo
+   |         ^^^^^^^ doc comments are not allowed here
+
+error: documentation comments cannot be applied to function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:96:9
+   |
+LL |         /// Bar
+   |         ^^^^^^^ doc comments are not allowed here
+
+error: documentation comments cannot be applied to function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:100:9
+   |
+LL |         /// Baz
+   |         ^^^^^^^ doc comments are not allowed here
+
+error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:102:9
+   |
+LL |         #[must_use]
+   |         ^^^^^^^^^^^
+
+error: documentation comments cannot be applied to function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:104:9
+   |
+LL |         /// Qux
+   |         ^^^^^^^ doc comments are not allowed here
+
+error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:106:9
+   |
+LL |         #[no_mangle] b: i32,
+   |         ^^^^^^^^^^^^
+
+error: documentation comments cannot be applied to function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:112:9
+   |
+LL |         /// Foo
+   |         ^^^^^^^ doc comments are not allowed here
+
+error: documentation comments cannot be applied to function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:115:9
+   |
+LL |         /// Bar
+   |         ^^^^^^^ doc comments are not allowed here
+
+error: documentation comments cannot be applied to function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:119:9
+   |
+LL |         /// Baz
+   |         ^^^^^^^ doc comments are not allowed here
+
+error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:121:9
+   |
+LL |         #[must_use]
+   |         ^^^^^^^^^^^
+
+error: documentation comments cannot be applied to function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:123:9
+   |
+LL |         /// Qux
+   |         ^^^^^^^ doc comments are not allowed here
+
+error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:125:9
+   |
+LL |         #[no_mangle] b: i32,
+   |         ^^^^^^^^^^^^
+
+error: documentation comments cannot be applied to function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:132:9
+   |
+LL |         /// Foo
+   |         ^^^^^^^ doc comments are not allowed here
+
+error: documentation comments cannot be applied to function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:136:9
+   |
+LL |         /// Bar
+   |         ^^^^^^^ doc comments are not allowed here
+
+error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:138:9
+   |
+LL |         #[must_use]
+   |         ^^^^^^^^^^^
+
+error: documentation comments cannot be applied to function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:140:9
+   |
+LL |         /// Baz
+   |         ^^^^^^^ doc comments are not allowed here
+
+error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
+  --> $DIR/param-attrs-builtin-attrs.rs:142:9
+   |
+LL |         #[no_mangle] b: i32
+   |         ^^^^^^^^^^^^
+
+error[E0658]: The attribute `test` is currently unknown to the compiler and may have meaning added to it in the future
+  --> $DIR/param-attrs-builtin-attrs.rs:7:9
+   |
+LL |         #[test] a: i32,
+   |         ^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/29642
+   = help: add #![feature(custom_attribute)] to the crate attributes to enable
+
+error[E0658]: The attribute `test` is currently unknown to the compiler and may have meaning added to it in the future
+  --> $DIR/param-attrs-builtin-attrs.rs:23:5
+   |
+LL |     #[test] a: u32,
+   |     ^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/29642
+   = help: add #![feature(custom_attribute)] to the crate attributes to enable
+
+error[E0658]: The attribute `test` is currently unknown to the compiler and may have meaning added to it in the future
+  --> $DIR/param-attrs-builtin-attrs.rs:38:5
+   |
+LL |     #[test] a: u32,
+   |     ^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/29642
+   = help: add #![feature(custom_attribute)] to the crate attributes to enable
+
+error[E0658]: The attribute `test` is currently unknown to the compiler and may have meaning added to it in the future
+  --> $DIR/param-attrs-builtin-attrs.rs:58:9
+   |
+LL |         #[test] a: i32,
+   |         ^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/29642
+   = help: add #![feature(custom_attribute)] to the crate attributes to enable
+
+error[E0658]: The attribute `test` is currently unknown to the compiler and may have meaning added to it in the future
+  --> $DIR/param-attrs-builtin-attrs.rs:79:9
+   |
+LL |         #[test] a: i32,
+   |         ^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/29642
+   = help: add #![feature(custom_attribute)] to the crate attributes to enable
+
+error[E0658]: The attribute `test` is currently unknown to the compiler and may have meaning added to it in the future
+  --> $DIR/param-attrs-builtin-attrs.rs:98:9
+   |
+LL |         #[test] a: i32,
+   |         ^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/29642
+   = help: add #![feature(custom_attribute)] to the crate attributes to enable
+
+error[E0658]: The attribute `test` is currently unknown to the compiler and may have meaning added to it in the future
+  --> $DIR/param-attrs-builtin-attrs.rs:117:9
+   |
+LL |         #[test] a: i32,
+   |         ^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/29642
+   = help: add #![feature(custom_attribute)] to the crate attributes to enable
+
+error[E0658]: The attribute `test` is currently unknown to the compiler and may have meaning added to it in the future
+  --> $DIR/param-attrs-builtin-attrs.rs:134:9
+   |
+LL |         #[test] a: u32,
+   |         ^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/29642
+   = help: add #![feature(custom_attribute)] to the crate attributes to enable
+
+error: aborting due to 52 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/rfc-2565-param-attrs/param-attrs-cfg.rs b/src/test/ui/rfc-2565-param-attrs/param-attrs-cfg.rs
new file mode 100644
index 00000000000..977b5d9ce34
--- /dev/null
+++ b/src/test/ui/rfc-2565-param-attrs/param-attrs-cfg.rs
@@ -0,0 +1,79 @@
+// compile-flags: --cfg something
+
+#![feature(param_attrs)]
+#![deny(unused_variables)]
+
+extern "C" {
+    fn ffi(
+        #[cfg(nothing)] a: i32,
+        #[cfg(something)] b: i32,
+        #[cfg_attr(something, cfg(nothing))] c: i32,
+        #[cfg_attr(nothing, cfg(nothing))] ...
+    );
+}
+
+type FnType = fn(
+    #[cfg(nothing)] a: i32,
+    #[cfg(something)] b: i32,
+    #[cfg_attr(nothing, cfg(nothing))] c: i32,
+    #[cfg_attr(something, cfg(nothing))] d: i32,
+);
+
+fn foo(
+    #[cfg(nothing)] a: i32,
+    #[cfg(something)] b: i32,
+    //~^ ERROR unused variable: `b` [unused_variables]
+    #[cfg_attr(nothing, cfg(nothing))] c: i32,
+    //~^ ERROR unused variable: `c` [unused_variables]
+    #[cfg_attr(something, cfg(nothing))] d: i32,
+) {}
+
+struct RefStruct {}
+impl RefStruct {
+    fn bar(
+        &self,
+        #[cfg(nothing)] a: i32,
+        #[cfg(something)] b: i32,
+        //~^ ERROR unused variable: `b` [unused_variables]
+        #[cfg_attr(nothing, cfg(nothing))] c: i32,
+        //~^ ERROR unused variable: `c` [unused_variables]
+        #[cfg_attr(something, cfg(nothing))] d: i32,
+    ) {}
+}
+trait RefTrait {
+    fn bar(
+        &self,
+        #[cfg(nothing)] a: i32,
+        #[cfg(something)] b: i32,
+        //~^ ERROR unused variable: `b` [unused_variables]
+        #[cfg_attr(nothing, cfg(nothing))] c: i32,
+        //~^ ERROR unused variable: `c` [unused_variables]
+        #[cfg_attr(something, cfg(nothing))] d: i32,
+    ) {}
+}
+impl RefTrait for RefStruct {
+    fn bar(
+        &self,
+        #[cfg(nothing)] a: i32,
+        #[cfg(something)] b: i32,
+        //~^ ERROR unused variable: `b` [unused_variables]
+        #[cfg_attr(nothing, cfg(nothing))] c: i32,
+        //~^ ERROR unused variable: `c` [unused_variables]
+        #[cfg_attr(something, cfg(nothing))] d: i32,
+    ) {}
+}
+
+fn main() {
+    let _: unsafe extern "C" fn(_, ...) = ffi;
+    let _: fn(_, _) = foo;
+    let _: FnType = |_, _| {};
+    let c = |
+        #[cfg(nothing)] a: i32,
+        #[cfg(something)] b: i32,
+        //~^ ERROR unused variable: `b` [unused_variables]
+        #[cfg_attr(nothing, cfg(nothing))] c: i32,
+        //~^ ERROR unused variable: `c` [unused_variables]
+        #[cfg_attr(something, cfg(nothing))] d: i32,
+    | {};
+    let _ = c(1, 2);
+}
diff --git a/src/test/ui/rfc-2565-param-attrs/param-attrs-cfg.stderr b/src/test/ui/rfc-2565-param-attrs/param-attrs-cfg.stderr
new file mode 100644
index 00000000000..c97190324e5
--- /dev/null
+++ b/src/test/ui/rfc-2565-param-attrs/param-attrs-cfg.stderr
@@ -0,0 +1,68 @@
+error: unused variable: `b`
+  --> $DIR/param-attrs-cfg.rs:24:23
+   |
+LL |     #[cfg(something)] b: i32,
+   |                       ^ help: consider prefixing with an underscore: `_b`
+   |
+note: lint level defined here
+  --> $DIR/param-attrs-cfg.rs:4:9
+   |
+LL | #![deny(unused_variables)]
+   |         ^^^^^^^^^^^^^^^^
+
+error: unused variable: `c`
+  --> $DIR/param-attrs-cfg.rs:26:40
+   |
+LL |     #[cfg_attr(nothing, cfg(nothing))] c: i32,
+   |                                        ^ help: consider prefixing with an underscore: `_c`
+
+error: unused variable: `b`
+  --> $DIR/param-attrs-cfg.rs:72:27
+   |
+LL |         #[cfg(something)] b: i32,
+   |                           ^ help: consider prefixing with an underscore: `_b`
+
+error: unused variable: `c`
+  --> $DIR/param-attrs-cfg.rs:74:44
+   |
+LL |         #[cfg_attr(nothing, cfg(nothing))] c: i32,
+   |                                            ^ help: consider prefixing with an underscore: `_c`
+
+error: unused variable: `b`
+  --> $DIR/param-attrs-cfg.rs:47:27
+   |
+LL |         #[cfg(something)] b: i32,
+   |                           ^ help: consider prefixing with an underscore: `_b`
+
+error: unused variable: `c`
+  --> $DIR/param-attrs-cfg.rs:49:44
+   |
+LL |         #[cfg_attr(nothing, cfg(nothing))] c: i32,
+   |                                            ^ help: consider prefixing with an underscore: `_c`
+
+error: unused variable: `b`
+  --> $DIR/param-attrs-cfg.rs:36:27
+   |
+LL |         #[cfg(something)] b: i32,
+   |                           ^ help: consider prefixing with an underscore: `_b`
+
+error: unused variable: `c`
+  --> $DIR/param-attrs-cfg.rs:38:44
+   |
+LL |         #[cfg_attr(nothing, cfg(nothing))] c: i32,
+   |                                            ^ help: consider prefixing with an underscore: `_c`
+
+error: unused variable: `b`
+  --> $DIR/param-attrs-cfg.rs:58:27
+   |
+LL |         #[cfg(something)] b: i32,
+   |                           ^ help: consider prefixing with an underscore: `_b`
+
+error: unused variable: `c`
+  --> $DIR/param-attrs-cfg.rs:60:44
+   |
+LL |         #[cfg_attr(nothing, cfg(nothing))] c: i32,
+   |                                            ^ help: consider prefixing with an underscore: `_c`
+
+error: aborting due to 10 previous errors
+
diff --git a/src/test/ui/rfc-2565-param-attrs/param-attrs-feature-gate.rs b/src/test/ui/rfc-2565-param-attrs/param-attrs-feature-gate.rs
new file mode 100644
index 00000000000..c5a6514efb0
--- /dev/null
+++ b/src/test/ui/rfc-2565-param-attrs/param-attrs-feature-gate.rs
@@ -0,0 +1,14 @@
+// gate-test-param_attrs
+
+fn foo(
+    /// Foo
+    //~^ ERROR documentation comments cannot be applied to function parameters
+    //~| NOTE doc comments are not allowed here
+    //~| ERROR attributes on function parameters are unstable
+    //~| NOTE https://github.com/rust-lang/rust/issues/60406
+    #[allow(C)] a: u8
+    //~^ ERROR attributes on function parameters are unstable
+    //~| NOTE https://github.com/rust-lang/rust/issues/60406
+) {}
+
+fn main() {}
diff --git a/src/test/ui/rfc-2565-param-attrs/param-attrs-feature-gate.stderr b/src/test/ui/rfc-2565-param-attrs/param-attrs-feature-gate.stderr
new file mode 100644
index 00000000000..82f21e7fdbc
--- /dev/null
+++ b/src/test/ui/rfc-2565-param-attrs/param-attrs-feature-gate.stderr
@@ -0,0 +1,27 @@
+error: documentation comments cannot be applied to function parameters
+  --> $DIR/param-attrs-feature-gate.rs:4:5
+   |
+LL |     /// Foo
+   |     ^^^^^^^ doc comments are not allowed here
+
+error[E0658]: attributes on function parameters are unstable
+  --> $DIR/param-attrs-feature-gate.rs:4:5
+   |
+LL |     /// Foo
+   |     ^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/60406
+   = help: add #![feature(param_attrs)] to the crate attributes to enable
+
+error[E0658]: attributes on function parameters are unstable
+  --> $DIR/param-attrs-feature-gate.rs:9:5
+   |
+LL |     #[allow(C)] a: u8
+   |     ^^^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/60406
+   = help: add #![feature(param_attrs)] to the crate attributes to enable
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.