about summary refs log tree commit diff
diff options
context:
space:
mode:
authorCaio <c410.f3r@gmail.com>2019-07-26 19:52:37 -0300
committerCaio <c410.f3r@gmail.com>2019-07-27 07:16:21 -0300
commit53fc7fbc9606ba8b29e674ab08c3ccf1ebfd128d (patch)
treea4ed14e5c8281e129d4fe42d69a95ff6aa0f74fa
parenta7f28678bbf4e16893bb6a718e427504167a9494 (diff)
downloadrust-53fc7fbc9606ba8b29e674ab08c3ccf1ebfd128d.tar.gz
rust-53fc7fbc9606ba8b29e674ab08c3ccf1ebfd128d.zip
Lint attributes on function arguments
-rw-r--r--src/librustc/hir/intravisit.rs15
-rw-r--r--src/librustc/hir/lowering.rs43
-rw-r--r--src/librustc/hir/map/collector.rs8
-rw-r--r--src/librustc/hir/map/mod.rs7
-rw-r--r--src/librustc/hir/mod.rs5
-rw-r--r--src/librustc/hir/print.rs5
-rw-r--r--src/librustc/lint/context.rs14
-rw-r--r--src/librustc/lint/mod.rs10
-rw-r--r--src/librustc_mir/build/mod.rs47
-rw-r--r--src/librustc_passes/hir_stats.rs5
-rw-r--r--src/librustc_typeck/check/_match.rs25
-rw-r--r--src/librustc_typeck/check/demand.rs82
-rw-r--r--src/libsyntax/ast.rs2
-rw-r--r--src/libsyntax/ext/build.rs1
-rw-r--r--src/libsyntax/mut_visit.rs3
-rw-r--r--src/libsyntax/parse/diagnostics.rs2
-rw-r--r--src/libsyntax/parse/parser.rs8
-rw-r--r--src/libsyntax/visit.rs15
-rw-r--r--src/test/ui/lint/lint-unused-mut-variables.rs64
-rw-r--r--src/test/ui/lint/lint-unused-mut-variables.stderr112
-rw-r--r--src/test/ui/lint/lint-unused-variables.rs64
-rw-r--r--src/test/ui/lint/lint-unused-variables.stderr56
-rw-r--r--src/test/ui/mismatched_types/issue-38371.stderr6
-rw-r--r--src/test/ui/rfc-2565-param-attrs/param-attrs-allowed.rs179
-rw-r--r--src/test/ui/rfc-2565-param-attrs/param-attrs-cfg.rs40
-rw-r--r--src/test/ui/rfc-2565-param-attrs/param-attrs-cfg.stderr46
-rw-r--r--src/test/ui/rfc-2565-param-attrs/param-attrs-feature-gate.rs4
-rw-r--r--src/test/ui/rfc-2565-param-attrs/param-attrs-feature-gate.stderr10
28 files changed, 586 insertions, 292 deletions
diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs
index 2d82314f86a..3781d7df176 100644
--- a/src/librustc/hir/intravisit.rs
+++ b/src/librustc/hir/intravisit.rs
@@ -210,6 +210,10 @@ pub trait Visitor<'v> : Sized {
         }
     }
 
+    fn visit_arg(&mut self, arg: &'v Arg) {
+        walk_arg(self, arg)
+    }
+
     /// Visits the top-level item and (optionally) nested items / impl items. See
     /// `visit_nested_item` for details.
     fn visit_item(&mut self, i: &'v Item) {
@@ -396,10 +400,7 @@ pub fn walk_mod<'v, V: Visitor<'v>>(visitor: &mut V, module: &'v Mod, mod_hir_id
 }
 
 pub fn walk_body<'v, V: Visitor<'v>>(visitor: &mut V, body: &'v Body) {
-    for argument in &body.arguments {
-        visitor.visit_id(argument.hir_id);
-        visitor.visit_pat(&argument.pat);
-    }
+    walk_list!(visitor, visit_arg, &body.arguments);
     visitor.visit_expr(&body.value);
 }
 
@@ -452,6 +453,12 @@ pub fn walk_trait_ref<'v, V>(visitor: &mut V, trait_ref: &'v TraitRef)
     visitor.visit_path(&trait_ref.path, trait_ref.hir_ref_id)
 }
 
+pub fn walk_arg<'v, V: Visitor<'v>>(visitor: &mut V, arg: &'v Arg) {
+    visitor.visit_id(arg.hir_id);
+    visitor.visit_pat(&arg.pat);
+    walk_list!(visitor, visit_attribute, &arg.attrs);
+}
+
 pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) {
     visitor.visit_vis(&item.vis);
     visitor.visit_ident(item.ident);
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index 288fd2714e2..e1fc61b8254 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -2461,8 +2461,10 @@ impl<'a> LoweringContext<'a> {
 
     fn lower_arg(&mut self, arg: &Arg) -> hir::Arg {
         hir::Arg {
+            attrs: self.lower_attrs(&arg.attrs),
             hir_id: self.lower_node_id(arg.id),
             pat: self.lower_pat(&arg.pat),
+            span: arg.span,
         }
     }
 
@@ -3279,19 +3281,29 @@ impl<'a> LoweringContext<'a> {
                 //
                 // If this is the simple case, this argument will end up being the same as the
                 // original argument, but with a different pattern id.
+                let mut stmt_attrs = ThinVec::new();
+                stmt_attrs.extend(argument.attrs.iter().cloned());
                 let (new_argument_pat, new_argument_id) = this.pat_ident(desugared_span, ident);
                 let new_argument = hir::Arg {
+                    attrs: argument.attrs,
                     hir_id: argument.hir_id,
                     pat: new_argument_pat,
+                    span: argument.span,
                 };
 
+
                 if is_simple_argument {
                     // If this is the simple case, then we only insert one statement that is
                     // `let <pat> = <pat>;`. We re-use the original argument's pattern so that
                     // `HirId`s are densely assigned.
                     let expr = this.expr_ident(desugared_span, ident, new_argument_id);
                     let stmt = this.stmt_let_pat(
-                        desugared_span, Some(P(expr)), argument.pat, hir::LocalSource::AsyncFn);
+                        stmt_attrs,
+                        desugared_span,
+                        Some(P(expr)),
+                        argument.pat,
+                        hir::LocalSource::AsyncFn
+                    );
                     statements.push(stmt);
                 } else {
                     // If this is not the simple case, then we construct two statements:
@@ -3313,14 +3325,23 @@ impl<'a> LoweringContext<'a> {
                         desugared_span, ident, hir::BindingAnnotation::Mutable);
                     let move_expr = this.expr_ident(desugared_span, ident, new_argument_id);
                     let move_stmt = this.stmt_let_pat(
-                        desugared_span, Some(P(move_expr)), move_pat, hir::LocalSource::AsyncFn);
+                        ThinVec::new(),
+                        desugared_span,
+                        Some(P(move_expr)),
+                        move_pat,
+                        hir::LocalSource::AsyncFn
+                    );
 
                     // Construct the `let <pat> = __argN;` statement. We re-use the original
                     // argument's pattern so that `HirId`s are densely assigned.
                     let pattern_expr = this.expr_ident(desugared_span, ident, move_id);
                     let pattern_stmt = this.stmt_let_pat(
-                        desugared_span, Some(P(pattern_expr)), argument.pat,
-                        hir::LocalSource::AsyncFn);
+                        stmt_attrs,
+                        desugared_span,
+                        Some(P(pattern_expr)),
+                        argument.pat,
+                        hir::LocalSource::AsyncFn
+                    );
 
                     statements.push(move_stmt);
                     statements.push(pattern_stmt);
@@ -4910,6 +4931,7 @@ impl<'a> LoweringContext<'a> {
 
                 // `let mut __next`
                 let next_let = self.stmt_let_pat(
+                    ThinVec::new(),
                     desugared_span,
                     None,
                     next_pat,
@@ -4919,6 +4941,7 @@ impl<'a> LoweringContext<'a> {
                 // `let <pat> = __next`
                 let pat = self.lower_pat(pat);
                 let pat_let = self.stmt_let_pat(
+                    ThinVec::new(),
                     head_sp,
                     Some(next_expr),
                     pat,
@@ -5413,19 +5436,20 @@ impl<'a> LoweringContext<'a> {
 
     fn stmt_let_pat(
         &mut self,
+        attrs: ThinVec<Attribute>,
         span: Span,
         init: Option<P<hir::Expr>>,
         pat: P<hir::Pat>,
         source: hir::LocalSource,
     ) -> hir::Stmt {
         let local = hir::Local {
-            pat,
-            ty: None,
-            init,
+            attrs,
             hir_id: self.next_id(),
-            span,
+            init,
+            pat,
             source,
-            attrs: ThinVec::new()
+            span,
+            ty: None,
         };
         self.stmt(span, hir::StmtKind::Local(P(local)))
     }
@@ -5831,6 +5855,7 @@ impl<'a> LoweringContext<'a> {
             hir::BindingAnnotation::Mutable,
         );
         let pinned_let = self.stmt_let_pat(
+            ThinVec::new(),
             span,
             Some(expr),
             pinned_pat,
diff --git a/src/librustc/hir/map/collector.rs b/src/librustc/hir/map/collector.rs
index 12ea772c1fb..b6807f7d3bb 100644
--- a/src/librustc/hir/map/collector.rs
+++ b/src/librustc/hir/map/collector.rs
@@ -363,6 +363,14 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
         self.currently_in_body = prev_in_body;
     }
 
+    fn visit_arg(&mut self, arg: &'hir Arg) {
+        let node = Node::Arg(arg);
+        self.insert(arg.pat.span, arg.hir_id, node);
+        self.with_parent(arg.hir_id, |this| {
+            intravisit::walk_arg(this, arg);
+        });
+    }
+
     fn visit_item(&mut self, i: &'hir Item) {
         debug!("visit_item: {:?}", i);
         debug_assert_eq!(i.hir_id.owner,
diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs
index 43b1dbb6216..5a28d9e7b7d 100644
--- a/src/librustc/hir/map/mod.rs
+++ b/src/librustc/hir/map/mod.rs
@@ -360,6 +360,7 @@ impl<'hir> Map<'hir> {
             Node::Pat(_) |
             Node::Binding(_) |
             Node::Local(_) |
+            Node::Arg(_) |
             Node::Arm(_) |
             Node::Lifetime(_) |
             Node::Visibility(_) |
@@ -932,6 +933,7 @@ impl<'hir> Map<'hir> {
     pub fn attrs(&self, id: HirId) -> &'hir [ast::Attribute] {
         self.read(id); // reveals attributes on the node
         let attrs = match self.find_entry(id).map(|entry| entry.node) {
+            Some(Node::Arg(a)) => Some(&a.attrs[..]),
             Some(Node::Local(l)) => Some(&l.attrs[..]),
             Some(Node::Item(i)) => Some(&i.attrs[..]),
             Some(Node::ForeignItem(fi)) => Some(&fi.attrs[..]),
@@ -995,6 +997,7 @@ impl<'hir> Map<'hir> {
     pub fn span(&self, hir_id: HirId) -> Span {
         self.read(hir_id); // reveals span from node
         match self.find_entry(hir_id).map(|entry| entry.node) {
+            Some(Node::Arg(arg)) => arg.span,
             Some(Node::Item(item)) => item.span,
             Some(Node::ForeignItem(foreign_item)) => foreign_item.span,
             Some(Node::TraitItem(trait_method)) => trait_method.span,
@@ -1197,6 +1200,7 @@ impl<'hir> print::PpAnn for Map<'hir> {
 impl<'a> print::State<'a> {
     pub fn print_node(&mut self, node: Node<'_>) {
         match node {
+            Node::Arg(a)          => self.print_arg(&a),
             Node::Item(a)         => self.print_item(&a),
             Node::ForeignItem(a)  => self.print_foreign_item(&a),
             Node::TraitItem(a)    => self.print_trait_item(a),
@@ -1338,6 +1342,9 @@ fn hir_id_to_string(map: &Map<'_>, id: HirId, include_id: bool) -> String {
         Some(Node::Pat(_)) => {
             format!("pat {}{}", map.hir_to_pretty_string(id), id_str)
         }
+        Some(Node::Arg(_)) => {
+            format!("arg {}{}", map.hir_to_pretty_string(id), id_str)
+        }
         Some(Node::Arm(_)) => {
             format!("arm {}{}", map.hir_to_pretty_string(id), id_str)
         }
diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs
index e7b37d40b4b..821585da4eb 100644
--- a/src/librustc/hir/mod.rs
+++ b/src/librustc/hir/mod.rs
@@ -2010,8 +2010,10 @@ pub struct InlineAsm {
 /// Represents an argument in a function header.
 #[derive(RustcEncodable, RustcDecodable, Debug, HashStable)]
 pub struct Arg {
-    pub pat: P<Pat>,
+    pub attrs: HirVec<Attribute>,
     pub hir_id: HirId,
+    pub pat: P<Pat>,
+    pub span: Span,
 }
 
 /// Represents the header (not the body) of a function declaration.
@@ -2701,6 +2703,7 @@ impl CodegenFnAttrs {
 
 #[derive(Copy, Clone, Debug)]
 pub enum Node<'hir> {
+    Arg(&'hir Arg),
     Item(&'hir Item),
     ForeignItem(&'hir ForeignItem),
     TraitItem(&'hir TraitItem),
diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs
index 3e571baaa4e..9f6be0a9290 100644
--- a/src/librustc/hir/print.rs
+++ b/src/librustc/hir/print.rs
@@ -1767,6 +1767,11 @@ impl<'a> State<'a> {
         self.ann.post(self, AnnNode::Pat(pat))
     }
 
+    pub fn print_arg(&mut self, arg: &hir::Arg) {
+        self.print_outer_attributes(&arg.attrs);
+        self.print_pat(&arg.pat);
+    }
+
     pub fn print_arm(&mut self, arm: &hir::Arm) {
         // I have no idea why this check is necessary, but here it
         // is :(
diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs
index 2930f7690dd..597ea8e8e5f 100644
--- a/src/librustc/lint/context.rs
+++ b/src/librustc/lint/context.rs
@@ -966,6 +966,13 @@ for LateContextAndPass<'a, 'tcx, T> {
         self.context.tables = old_tables;
     }
 
+    fn visit_arg(&mut self, arg: &'tcx hir::Arg) {
+        self.with_lint_attrs(arg.hir_id, &arg.attrs, |cx| {
+            lint_callback!(cx, check_arg, arg);
+            hir_visit::walk_arg(cx, arg);
+        });
+    }
+
     fn visit_body(&mut self, body: &'tcx hir::Body) {
         lint_callback!(self, check_body, body);
         hir_visit::walk_body(self, body);
@@ -1156,6 +1163,13 @@ for LateContextAndPass<'a, 'tcx, T> {
 }
 
 impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> {
+    fn visit_arg(&mut self, arg: &'a ast::Arg) {
+        self.with_lint_attrs(arg.id, &arg.attrs, |cx| {
+            run_early_pass!(cx, check_arg, arg);
+            ast_visit::walk_arg(cx, arg);
+        });
+    }
+
     fn visit_item(&mut self, it: &'a ast::Item) {
         self.with_lint_attrs(it.id, &it.attrs, |cx| {
             run_early_pass!(cx, check_item, it);
diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs
index dddbc31ccd4..8ddf4603490 100644
--- a/src/librustc/lint/mod.rs
+++ b/src/librustc/lint/mod.rs
@@ -206,6 +206,7 @@ macro_rules! declare_lint_pass {
 macro_rules! late_lint_methods {
     ($macro:path, $args:tt, [$hir:tt]) => (
         $macro!($args, [$hir], [
+            fn check_arg(a: &$hir hir::Arg);
             fn check_body(a: &$hir hir::Body);
             fn check_body_post(a: &$hir hir::Body);
             fn check_name(a: Span, b: ast::Name);
@@ -358,6 +359,7 @@ macro_rules! declare_combined_late_lint_pass {
 macro_rules! early_lint_methods {
     ($macro:path, $args:tt) => (
         $macro!($args, [
+            fn check_arg(a: &ast::Arg);
             fn check_ident(a: ast::Ident);
             fn check_crate(a: &ast::Crate);
             fn check_crate_post(a: &ast::Crate);
@@ -495,8 +497,6 @@ pub type EarlyLintPassObject = Box<dyn EarlyLintPass + sync::Send + sync::Sync +
 pub type LateLintPassObject = Box<dyn for<'a, 'tcx> LateLintPass<'a, 'tcx> + sync::Send
                                                                            + sync::Sync + 'static>;
 
-
-
 /// Identifies a lint known to the compiler.
 #[derive(Clone, Copy, Debug)]
 pub struct LintId {
@@ -812,6 +812,12 @@ impl intravisit::Visitor<'tcx> for LintLevelMapBuilder<'tcx> {
         intravisit::NestedVisitorMap::All(&self.tcx.hir())
     }
 
+    fn visit_arg(&mut self, arg: &'tcx hir::Arg) {
+        self.with_lint_attrs(arg.hir_id, &arg.attrs, |builder| {
+            intravisit::walk_arg(builder, arg);
+        });
+    }
+
     fn visit_item(&mut self, it: &'tcx hir::Item) {
         self.with_lint_attrs(it.hir_id, &it.attrs, |builder| {
             intravisit::walk_item(builder, it);
diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs
index 8948d1c4b36..4e970aee42c 100644
--- a/src/librustc_mir/build/mod.rs
+++ b/src/librustc_mir/build/mod.rs
@@ -121,7 +121,7 @@ pub fn mir_build(tcx: TyCtxt<'_>, def_id: DefId) -> Body<'_> {
                             self_arg = None;
                         }
 
-                        ArgInfo(fn_sig.inputs()[index], opt_ty_info, Some(&*arg.pat), self_arg)
+                        ArgInfo(fn_sig.inputs()[index], opt_ty_info, Some(&arg), self_arg)
                     });
 
             let arguments = implicit_argument.into_iter().chain(explicit_arguments);
@@ -511,7 +511,7 @@ fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: DefId, abi: Abi) -> bool {
 ///////////////////////////////////////////////////////////////////////////
 /// the main entry point for building MIR for a function
 
-struct ArgInfo<'tcx>(Ty<'tcx>, Option<Span>, Option<&'tcx hir::Pat>, Option<ImplicitSelfKind>);
+struct ArgInfo<'tcx>(Ty<'tcx>, Option<Span>, Option<&'tcx hir::Arg>, Option<ImplicitSelfKind>);
 
 fn construct_fn<'a, 'tcx, A>(
     hir: Cx<'a, 'tcx>,
@@ -782,13 +782,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                      -> BlockAnd<()>
     {
         // Allocate locals for the function arguments
-        for &ArgInfo(ty, _, pattern, _) in arguments.iter() {
+        for &ArgInfo(ty, _, arg_opt, _) in arguments.iter() {
             // If this is a simple binding pattern, give the local a name for
             // debuginfo and so that error reporting knows that this is a user
             // variable. For any other pattern the pattern introduces new
             // variables which will be named instead.
-            let (name, span) = if let Some(pat) = pattern {
-                (pat.simple_ident().map(|ident| ident.name), pat.span)
+            let (name, span) = if let Some(arg) = arg_opt {
+                (arg.pat.simple_ident().map(|ident| ident.name), arg.pat.span)
             } else {
                 (None, self.fn_span)
             };
@@ -813,18 +813,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             // Function arguments always get the first Local indices after the return place
             let local = Local::new(index + 1);
             let place = Place::from(local);
-            let &ArgInfo(ty, opt_ty_info, pattern, ref self_binding) = arg_info;
+            let &ArgInfo(ty, opt_ty_info, arg_opt, ref self_binding) = arg_info;
 
             // Make sure we drop (parts of) the argument even when not matched on.
             self.schedule_drop(
-                pattern.as_ref().map_or(ast_body.span, |pat| pat.span),
+                arg_opt.as_ref().map_or(ast_body.span, |arg| arg.pat.span),
                 argument_scope, local, ty, DropKind::Value,
             );
 
-            if let Some(pattern) = pattern {
-                let pattern = self.hir.pattern_from_hir(pattern);
+            if let Some(arg) = arg_opt {
+                let pattern = self.hir.pattern_from_hir(&arg.pat);
+                let original_source_scope = self.source_scope;
                 let span = pattern.span;
-
+                self.set_correct_source_scope_for_arg(arg.hir_id, original_source_scope, span);
                 match *pattern.kind {
                     // Don't introduce extra copies for simple bindings
                     PatternKind::Binding {
@@ -835,6 +836,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                         ..
                     } => {
                         self.local_decls[local].mutability = mutability;
+                        self.local_decls[local].source_info.scope = self.source_scope;
                         self.local_decls[local].is_user_variable =
                             if let Some(kind) = self_binding {
                                 Some(ClearCrossCrate::Set(BindingForm::ImplicitSelf(*kind)))
@@ -860,6 +862,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                         unpack!(block = self.place_into_pattern(block, pattern, &place, false));
                     }
                 }
+                self.source_scope = original_source_scope;
             }
         }
 
@@ -872,6 +875,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         self.into(&Place::RETURN_PLACE, block, body)
     }
 
+    fn set_correct_source_scope_for_arg(
+        &mut self,
+        arg_hir_id: hir::HirId,
+        original_source_scope: SourceScope,
+        pattern_span: Span
+    ) {
+        let tcx = self.hir.tcx();
+        let current_root = tcx.maybe_lint_level_root_bounded(
+            arg_hir_id,
+            self.hir.root_lint_level
+        );
+        let parent_root = tcx.maybe_lint_level_root_bounded(
+            self.source_scope_local_data[original_source_scope].lint_root,
+            self.hir.root_lint_level,
+        );
+        if current_root != parent_root {
+            self.source_scope = self.new_source_scope(
+                pattern_span,
+                LintLevel::Explicit(current_root),
+                None
+            );
+        }
+    }
+
     fn get_unit_temp(&mut self) -> Place<'tcx> {
         match self.unit_temp {
             Some(ref tmp) => tmp.clone(),
diff --git a/src/librustc_passes/hir_stats.rs b/src/librustc_passes/hir_stats.rs
index e7f6abc410a..8fba3256ec4 100644
--- a/src/librustc_passes/hir_stats.rs
+++ b/src/librustc_passes/hir_stats.rs
@@ -94,6 +94,11 @@ impl<'k> StatCollector<'k> {
 }
 
 impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
+    fn visit_arg(&mut self, arg: &'v hir::Arg) {
+        self.record("Arg", Id::Node(arg.hir_id), arg);
+        hir_visit::walk_arg(self, arg)
+    }
+
     fn nested_visit_map<'this>(&'this mut self) -> hir_visit::NestedVisitorMap<'this, 'v> {
         panic!("visit_nested_xxx must be manually implemented in this visitor")
     }
diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index de42a6a35c8..c8983fef2de 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -551,21 +551,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ) {
         let tcx = self.tcx;
         if let PatKind::Binding(..) = inner.node {
-            let parent_id = tcx.hir().get_parent_node(pat.hir_id);
-            let parent = tcx.hir().get(parent_id);
-            debug!("inner {:?} pat {:?} parent {:?}", inner, pat, parent);
-            match parent {
-                hir::Node::Item(hir::Item { node: hir::ItemKind::Fn(..), .. }) |
-                hir::Node::ForeignItem(hir::ForeignItem {
-                    node: hir::ForeignItemKind::Fn(..), ..
-                }) |
-                hir::Node::TraitItem(hir::TraitItem { node: hir::TraitItemKind::Method(..), .. }) |
-                hir::Node::ImplItem(hir::ImplItem { node: hir::ImplItemKind::Method(..), .. }) => {
-                    // this pat is likely an argument
+            let binding_parent_id = tcx.hir().get_parent_node(pat.hir_id);
+            let binding_parent = tcx.hir().get(binding_parent_id);
+            debug!("inner {:?} pat {:?} parent {:?}", inner, pat, binding_parent);
+            match binding_parent {
+                hir::Node::Arg(hir::Arg { span, .. }) => {
                     if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(inner.span) {
-                        // FIXME: turn into structured suggestion, will need a span that also
-                        // includes the the arg's type.
-                        err.help(&format!("did you mean `{}: &{}`?", snippet, expected));
+                        err.span_suggestion(
+                            *span,
+                            &format!("did you mean `{}`", snippet),
+                            format!(" &{}", expected),
+                            Applicability::MachineApplicable,
+                        );
                     }
                 }
                 hir::Node::Arm(_) |
diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs
index 14c38ae053d..3229d49841e 100644
--- a/src/librustc_typeck/check/demand.rs
+++ b/src/librustc_typeck/check/demand.rs
@@ -235,40 +235,56 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         expr: &hir::Expr,
     ) -> Option<(Span, &'static str, String)> {
-        if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.node {
-            if let hir::def::Res::Local(id) = path.res {
-                let parent = self.tcx.hir().get_parent_node(id);
-                if let Some(Node::Expr(hir::Expr {
-                    hir_id,
-                    node: hir::ExprKind::Closure(_, decl, ..),
-                    ..
-                })) = self.tcx.hir().find(parent) {
-                    let parent = self.tcx.hir().get_parent_node(*hir_id);
-                    if let (Some(Node::Expr(hir::Expr {
-                        node: hir::ExprKind::MethodCall(path, span, expr),
-                        ..
-                    })), 1) = (self.tcx.hir().find(parent), decl.inputs.len()) {
-                        let self_ty = self.tables.borrow().node_type(expr[0].hir_id);
-                        let self_ty = format!("{:?}", self_ty);
-                        let name = path.ident.as_str();
-                        let is_as_ref_able = (
-                            self_ty.starts_with("&std::option::Option") ||
-                            self_ty.starts_with("&std::result::Result") ||
-                            self_ty.starts_with("std::option::Option") ||
-                            self_ty.starts_with("std::result::Result")
-                        ) && (name == "map" || name == "and_then");
-                        match (is_as_ref_able, self.sess().source_map().span_to_snippet(*span)) {
-                            (true, Ok(src)) => {
-                                return Some((*span, "consider using `as_ref` instead",
-                                             format!("as_ref().{}", src)));
-                            },
-                            _ => ()
-                        }
-                    }
-                }
-            }
+        let path = match expr.node {
+            hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => path,
+            _ => return None
+        };
+
+        let local_id = match path.res {
+            hir::def::Res::Local(id) => id,
+            _ => return None
+        };
+
+        let local_parent = self.tcx.hir().get_parent_node(local_id);
+        let arg_hir_id = match self.tcx.hir().find(local_parent) {
+            Some(Node::Arg(hir::Arg { hir_id, .. })) => hir_id,
+            _ => return None
+        };
+
+        let arg_parent = self.tcx.hir().get_parent_node(*arg_hir_id);
+        let (expr_hir_id, closure_fn_decl) = match self.tcx.hir().find(arg_parent) {
+            Some(Node::Expr(
+                hir::Expr { hir_id, node: hir::ExprKind::Closure(_, decl, ..), .. }
+            )) => (hir_id, decl),
+            _ => return None
+        };
+
+        let expr_parent = self.tcx.hir().get_parent_node(*expr_hir_id);
+        let hir = self.tcx.hir().find(expr_parent);
+        let closure_params_len = closure_fn_decl.inputs.len();
+        let (method_path, method_span, method_expr) = match (hir, closure_params_len) {
+            (Some(Node::Expr(
+                hir::Expr { node: hir::ExprKind::MethodCall(path, span, expr), .. }
+            )), 1) => (path, span, expr),
+            _ => return None
+        };
+
+        let self_ty = self.tables.borrow().node_type(method_expr[0].hir_id);
+        let self_ty = format!("{:?}", self_ty);
+        let name = method_path.ident.as_str();
+        let is_as_ref_able = (
+            self_ty.starts_with("&std::option::Option") ||
+            self_ty.starts_with("&std::result::Result") ||
+            self_ty.starts_with("std::option::Option") ||
+            self_ty.starts_with("std::result::Result")
+        ) && (name == "map" || name == "and_then");
+        match (is_as_ref_able, self.sess().source_map().span_to_snippet(*method_span)) {
+            (true, Ok(src)) => {
+                let suggestion = format!("as_ref().{}", src);
+                Some((*method_span, "consider using `as_ref` instead", suggestion))
+            },
+            _ => None
         }
-        None
     }
 
     crate fn is_hir_id_from_struct_pattern_shorthand_field(
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index b9b43c89346..471a657c82a 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -1776,6 +1776,7 @@ pub struct Arg {
     pub ty: P<Ty>,
     pub pat: P<Pat>,
     pub id: NodeId,
+    pub span: Span,
 }
 
 /// Alternative representation for `Arg`s describing `self` parameter of methods.
@@ -1834,6 +1835,7 @@ impl Arg {
                 node: PatKind::Ident(BindingMode::ByValue(mutbl), eself_ident, None),
                 span,
             }),
+            span,
             ty,
             id: DUMMY_NODE_ID,
         };
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs
index baf1031de1e..e1f8310ee8d 100644
--- a/src/libsyntax/ext/build.rs
+++ b/src/libsyntax/ext/build.rs
@@ -966,6 +966,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
             attrs: ThinVec::default(),
             id: ast::DUMMY_NODE_ID,
             pat: arg_pat,
+            span,
             ty,
         }
     }
diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs
index dc656222fbc..23dfad32911 100644
--- a/src/libsyntax/mut_visit.rs
+++ b/src/libsyntax/mut_visit.rs
@@ -558,10 +558,11 @@ 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 { attrs, id, pat, ty }: &mut Arg, vis: &mut T) {
+pub fn noop_visit_arg<T: MutVisitor>(Arg { attrs, id, pat, span, ty }: &mut Arg, vis: &mut T) {
     vis.visit_id(id);
     visit_thin_attrs(attrs, vis);
     vis.visit_pat(pat);
+    vis.visit_span(span);
     vis.visit_ty(ty);
 }
 
diff --git a/src/libsyntax/parse/diagnostics.rs b/src/libsyntax/parse/diagnostics.rs
index f4fc87506f3..39cb5042fbc 100644
--- a/src/libsyntax/parse/diagnostics.rs
+++ b/src/libsyntax/parse/diagnostics.rs
@@ -30,7 +30,7 @@ crate fn dummy_arg(ident: Ident) -> Arg {
         span: ident.span,
         id: ast::DUMMY_NODE_ID
     };
-    Arg { attrs: ThinVec::default(), id: ast::DUMMY_NODE_ID, pat, ty: P(ty) }
+    Arg { attrs: ThinVec::default(), id: ast::DUMMY_NODE_ID, pat, span: ident.span, ty: P(ty) }
 }
 
 pub enum Error {
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index da388694637..ed4cb405fe7 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -1502,6 +1502,7 @@ impl<'a> Parser<'a> {
     where
         F: Fn(&token::Token) -> bool
     {
+        let lo = self.token.span;
         let attrs = self.parse_arg_attributes()?;
         if let Some(mut arg) = self.parse_self_arg()? {
             arg.attrs = attrs.into();
@@ -1565,11 +1566,14 @@ impl<'a> Parser<'a> {
             }
         };
 
-        Ok(Arg { attrs: attrs.into(), id: ast::DUMMY_NODE_ID, pat, ty })
+        let span = lo.to(self.token.span);
+
+        Ok(Arg { attrs: attrs.into(), id: ast::DUMMY_NODE_ID, pat, span, ty })
     }
 
     /// Parses an argument in a lambda header (e.g., `|arg, arg|`).
     fn parse_fn_block_arg(&mut self) -> PResult<'a, Arg> {
+        let lo = self.token.span;
         let attrs = self.parse_arg_attributes()?;
         let pat = self.parse_pat(Some("argument name"))?;
         let t = if self.eat(&token::Colon) {
@@ -1581,10 +1585,12 @@ impl<'a> Parser<'a> {
                 span: self.prev_span,
             })
         };
+        let span = lo.to(self.token.span);
         Ok(Arg {
             attrs: attrs.into(),
             ty: t,
             pat,
+            span,
             id: ast::DUMMY_NODE_ID
         })
     }
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 9ec9550f93a..5fc8bdb608e 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -66,6 +66,7 @@ pub trait Visitor<'ast>: Sized {
     fn visit_local(&mut self, l: &'ast Local) { walk_local(self, l) }
     fn visit_block(&mut self, b: &'ast Block) { walk_block(self, b) }
     fn visit_stmt(&mut self, s: &'ast Stmt) { walk_stmt(self, s) }
+    fn visit_arg(&mut self, arg: &'ast Arg) { walk_arg(self, arg) }
     fn visit_arm(&mut self, a: &'ast Arm) { walk_arm(self, a) }
     fn visit_pat(&mut self, p: &'ast Pat) { walk_pat(self, p) }
     fn visit_anon_const(&mut self, c: &'ast AnonConst) { walk_anon_const(self, c) }
@@ -549,12 +550,10 @@ 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);
+    for arg in &function_declaration.inputs {
+        visitor.visit_arg(arg);
     }
-    visitor.visit_fn_ret_ty(&function_declaration.output)
+    visitor.visit_fn_ret_ty(&function_declaration.output);
 }
 
 pub fn walk_fn<'a, V>(visitor: &mut V, kind: FnKind<'a>, declaration: &'a FnDecl, _span: Span)
@@ -824,6 +823,12 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
     visitor.visit_expr_post(expression)
 }
 
+pub fn walk_arg<'a, V: Visitor<'a>>(visitor: &mut V, arg: &'a Arg) {
+    walk_list!(visitor, visit_attribute, arg.attrs.iter());
+    visitor.visit_pat(&arg.pat);
+    visitor.visit_ty(&arg.ty);
+}
+
 pub fn walk_arm<'a, V: Visitor<'a>>(visitor: &mut V, arm: &'a Arm) {
     walk_list!(visitor, visit_pat, &arm.pats);
     if let Some(ref e) = &arm.guard {
diff --git a/src/test/ui/lint/lint-unused-mut-variables.rs b/src/test/ui/lint/lint-unused-mut-variables.rs
index 78609a6e24b..2957f931110 100644
--- a/src/test/ui/lint/lint-unused-mut-variables.rs
+++ b/src/test/ui/lint/lint-unused-mut-variables.rs
@@ -1,12 +1,70 @@
+// edition:2018
+
 // Exercise the unused_mut attribute in some positive and negative cases
 
-#![allow(unused_assignments)]
-#![allow(unused_variables)]
-#![allow(dead_code)]
 #![deny(unused_mut)]
+#![feature(async_await, async_closure, param_attrs)]
+
+async fn baz_async(
+    mut a: i32,
+    //~^ ERROR: variable does not need to be mutable
+    #[allow(unused_mut)] mut b: i32,
+) {}
+fn baz(
+    mut a: i32,
+    //~^ ERROR: variable does not need to be mutable
+    #[allow(unused_mut)] mut b: i32,
+    #[allow(unused_mut)] (mut c, d): (i32, i32)
+) {}
+
+struct RefStruct {}
+impl RefStruct {
+    async fn baz_async(
+        mut a: i32,
+        //~^ ERROR: variable does not need to be mutable
+        #[allow(unused_mut)] mut b: i32,
+    ) {}
+    fn baz(
+        &self,
+        mut a: i32,
+        //~^ ERROR: variable does not need to be mutable
+        #[allow(unused_mut)] mut b: i32,
+        #[allow(unused_mut)] (mut c, d): (i32, i32)
+    ) {}
+}
 
+trait RefTrait {
+    fn baz(
+        &self,
+        mut a: i32,
+        //~^ ERROR: variable does not need to be mutable
+        #[allow(unused_mut)] mut b: i32,
+        #[allow(unused_mut)] (mut c, d): (i32, i32)
+    ) {}
+}
+impl RefTrait for () {
+    fn baz(
+        &self,
+        mut a: i32,
+        //~^ ERROR: variable does not need to be mutable
+        #[allow(unused_mut)] mut b: i32,
+        #[allow(unused_mut)] (mut c, d): (i32, i32)
+    ) {}
+}
 
 fn main() {
+    let _ = async move |
+        mut a: i32,
+        //~^ ERROR: variable does not need to be mutable
+        #[allow(unused_mut)] mut b: i32,
+    | {};
+    let _ = |
+        mut a: i32,
+        //~^ ERROR: variable does not need to be mutable
+        #[allow(unused_mut)] mut b: i32,
+        #[allow(unused_mut)] (mut c, d): (i32, i32)
+    | {};
+
     // negative cases
     let mut a = 3; //~ ERROR: variable does not need to be mutable
 
diff --git a/src/test/ui/lint/lint-unused-mut-variables.stderr b/src/test/ui/lint/lint-unused-mut-variables.stderr
index 1a175c9683e..92c2b68652d 100644
--- a/src/test/ui/lint/lint-unused-mut-variables.stderr
+++ b/src/test/ui/lint/lint-unused-mut-variables.stderr
@@ -1,19 +1,83 @@
 error: variable does not need to be mutable
-  --> $DIR/lint-unused-mut-variables.rs:46:14
+  --> $DIR/lint-unused-mut-variables.rs:9:5
    |
-LL |     let x = |mut y: isize| 10;
-   |              ----^
-   |              |
-   |              help: remove this `mut`
+LL |     mut a: i32,
+   |     ----^
+   |     |
+   |     help: remove this `mut`
    |
 note: lint level defined here
-  --> $DIR/lint-unused-mut-variables.rs:6:9
+  --> $DIR/lint-unused-mut-variables.rs:5:9
    |
 LL | #![deny(unused_mut)]
    |         ^^^^^^^^^^
 
 error: variable does not need to be mutable
-  --> $DIR/lint-unused-mut-variables.rs:11:9
+  --> $DIR/lint-unused-mut-variables.rs:14:5
+   |
+LL |     mut a: i32,
+   |     ----^
+   |     |
+   |     help: remove this `mut`
+
+error: variable does not need to be mutable
+  --> $DIR/lint-unused-mut-variables.rs:23:9
+   |
+LL |         mut a: i32,
+   |         ----^
+   |         |
+   |         help: remove this `mut`
+
+error: variable does not need to be mutable
+  --> $DIR/lint-unused-mut-variables.rs:29:9
+   |
+LL |         mut a: i32,
+   |         ----^
+   |         |
+   |         help: remove this `mut`
+
+error: variable does not need to be mutable
+  --> $DIR/lint-unused-mut-variables.rs:39:9
+   |
+LL |         mut a: i32,
+   |         ----^
+   |         |
+   |         help: remove this `mut`
+
+error: variable does not need to be mutable
+  --> $DIR/lint-unused-mut-variables.rs:48:9
+   |
+LL |         mut a: i32,
+   |         ----^
+   |         |
+   |         help: remove this `mut`
+
+error: variable does not need to be mutable
+  --> $DIR/lint-unused-mut-variables.rs:57:9
+   |
+LL |         mut a: i32,
+   |         ----^
+   |         |
+   |         help: remove this `mut`
+
+error: variable does not need to be mutable
+  --> $DIR/lint-unused-mut-variables.rs:62:9
+   |
+LL |         mut a: i32,
+   |         ----^
+   |         |
+   |         help: remove this `mut`
+
+error: variable does not need to be mutable
+  --> $DIR/lint-unused-mut-variables.rs:104:14
+   |
+LL |     let x = |mut y: isize| 10;
+   |              ----^
+   |              |
+   |              help: remove this `mut`
+
+error: variable does not need to be mutable
+  --> $DIR/lint-unused-mut-variables.rs:69:9
    |
 LL |     let mut a = 3;
    |         ----^
@@ -21,7 +85,7 @@ LL |     let mut a = 3;
    |         help: remove this `mut`
 
 error: variable does not need to be mutable
-  --> $DIR/lint-unused-mut-variables.rs:13:9
+  --> $DIR/lint-unused-mut-variables.rs:71:9
    |
 LL |     let mut a = 2;
    |         ----^
@@ -29,7 +93,7 @@ LL |     let mut a = 2;
    |         help: remove this `mut`
 
 error: variable does not need to be mutable
-  --> $DIR/lint-unused-mut-variables.rs:15:9
+  --> $DIR/lint-unused-mut-variables.rs:73:9
    |
 LL |     let mut b = 3;
    |         ----^
@@ -37,7 +101,7 @@ LL |     let mut b = 3;
    |         help: remove this `mut`
 
 error: variable does not need to be mutable
-  --> $DIR/lint-unused-mut-variables.rs:17:9
+  --> $DIR/lint-unused-mut-variables.rs:75:9
    |
 LL |     let mut a = vec![3];
    |         ----^
@@ -45,7 +109,7 @@ LL |     let mut a = vec![3];
    |         help: remove this `mut`
 
 error: variable does not need to be mutable
-  --> $DIR/lint-unused-mut-variables.rs:19:10
+  --> $DIR/lint-unused-mut-variables.rs:77:10
    |
 LL |     let (mut a, b) = (1, 2);
    |          ----^
@@ -53,7 +117,7 @@ LL |     let (mut a, b) = (1, 2);
    |          help: remove this `mut`
 
 error: variable does not need to be mutable
-  --> $DIR/lint-unused-mut-variables.rs:21:9
+  --> $DIR/lint-unused-mut-variables.rs:79:9
    |
 LL |     let mut a;
    |         ----^
@@ -61,7 +125,7 @@ LL |     let mut a;
    |         help: remove this `mut`
 
 error: variable does not need to be mutable
-  --> $DIR/lint-unused-mut-variables.rs:25:9
+  --> $DIR/lint-unused-mut-variables.rs:83:9
    |
 LL |     let mut b;
    |         ----^
@@ -69,7 +133,7 @@ LL |     let mut b;
    |         help: remove this `mut`
 
 error: variable does not need to be mutable
-  --> $DIR/lint-unused-mut-variables.rs:34:9
+  --> $DIR/lint-unused-mut-variables.rs:92:9
    |
 LL |         mut x => {}
    |         ----^
@@ -77,7 +141,7 @@ LL |         mut x => {}
    |         help: remove this `mut`
 
 error: variable does not need to be mutable
-  --> $DIR/lint-unused-mut-variables.rs:38:8
+  --> $DIR/lint-unused-mut-variables.rs:96:8
    |
 LL |       (mut x, 1) |
    |        ----^
@@ -85,7 +149,7 @@ LL |       (mut x, 1) |
    |        help: remove this `mut`
 
 error: variable does not need to be mutable
-  --> $DIR/lint-unused-mut-variables.rs:51:9
+  --> $DIR/lint-unused-mut-variables.rs:109:9
    |
 LL |     let mut a = &mut 5;
    |         ----^
@@ -93,7 +157,7 @@ LL |     let mut a = &mut 5;
    |         help: remove this `mut`
 
 error: variable does not need to be mutable
-  --> $DIR/lint-unused-mut-variables.rs:56:9
+  --> $DIR/lint-unused-mut-variables.rs:114:9
    |
 LL |     let mut b = (&mut a,);
    |         ----^
@@ -101,7 +165,7 @@ LL |     let mut b = (&mut a,);
    |         help: remove this `mut`
 
 error: variable does not need to be mutable
-  --> $DIR/lint-unused-mut-variables.rs:59:9
+  --> $DIR/lint-unused-mut-variables.rs:117:9
    |
 LL |     let mut x = &mut 1;
    |         ----^
@@ -109,7 +173,7 @@ LL |     let mut x = &mut 1;
    |         help: remove this `mut`
 
 error: variable does not need to be mutable
-  --> $DIR/lint-unused-mut-variables.rs:71:9
+  --> $DIR/lint-unused-mut-variables.rs:129:9
    |
 LL |     let mut v : &mut Vec<()> = &mut vec![];
    |         ----^
@@ -117,7 +181,7 @@ LL |     let mut v : &mut Vec<()> = &mut vec![];
    |         help: remove this `mut`
 
 error: variable does not need to be mutable
-  --> $DIR/lint-unused-mut-variables.rs:48:13
+  --> $DIR/lint-unused-mut-variables.rs:106:13
    |
 LL |     fn what(mut foo: isize) {}
    |             ----^^^
@@ -125,7 +189,7 @@ LL |     fn what(mut foo: isize) {}
    |             help: remove this `mut`
 
 error: variable does not need to be mutable
-  --> $DIR/lint-unused-mut-variables.rs:66:20
+  --> $DIR/lint-unused-mut-variables.rs:124:20
    |
 LL |     fn mut_ref_arg(mut arg : &mut [u8]) -> &mut [u8] {
    |                    ----^^^
@@ -133,7 +197,7 @@ LL |     fn mut_ref_arg(mut arg : &mut [u8]) -> &mut [u8] {
    |                    help: remove this `mut`
 
 error: variable does not need to be mutable
-  --> $DIR/lint-unused-mut-variables.rs:138:9
+  --> $DIR/lint-unused-mut-variables.rs:196:9
    |
 LL |     let mut b = vec![2];
    |         ----^
@@ -141,10 +205,10 @@ LL |     let mut b = vec![2];
    |         help: remove this `mut`
    |
 note: lint level defined here
-  --> $DIR/lint-unused-mut-variables.rs:134:8
+  --> $DIR/lint-unused-mut-variables.rs:192:8
    |
 LL | #[deny(unused_mut)]
    |        ^^^^^^^^^^
 
-error: aborting due to 17 previous errors
+error: aborting due to 25 previous errors
 
diff --git a/src/test/ui/lint/lint-unused-variables.rs b/src/test/ui/lint/lint-unused-variables.rs
new file mode 100644
index 00000000000..a1660d23511
--- /dev/null
+++ b/src/test/ui/lint/lint-unused-variables.rs
@@ -0,0 +1,64 @@
+// compile-flags: --cfg something
+// edition:2018
+
+#![feature(async_await, async_closure, param_attrs)]
+#![deny(unused_variables)]
+
+async fn foo_async(
+    a: i32,
+    //~^ ERROR unused variable: `a`
+    #[allow(unused_variables)] b: i32,
+) {}
+fn foo(
+    #[allow(unused_variables)] a: i32,
+    b: i32,
+    //~^ ERROR unused variable: `b`
+) {}
+
+struct RefStruct {}
+impl RefStruct {
+    async fn bar_async(
+        &self,
+        a: i32,
+        //~^ ERROR unused variable: `a`
+        #[allow(unused_variables)] b: i32,
+    ) {}
+    fn bar(
+        &self,
+        #[allow(unused_variables)] a: i32,
+        b: i32,
+        //~^ ERROR unused variable: `b`
+    ) {}
+}
+trait RefTrait {
+    fn bar(
+        &self,
+        #[allow(unused_variables)] a: i32,
+        b: i32,
+        //~^ ERROR unused variable: `b`
+    ) {}
+}
+impl RefTrait for RefStruct {
+    fn bar(
+        &self,
+        #[allow(unused_variables)] a: i32,
+        b: i32,
+        //~^ ERROR unused variable: `b`
+    ) {}
+}
+
+fn main() {
+    let _: fn(_, _) = foo;
+    let a = async move |
+        a: i32,
+        //~^ ERROR unused variable: `a`
+        #[allow(unused_variables)] b: i32,
+    | {};
+    let b = |
+        #[allow(unused_variables)] a: i32,
+        b: i32,
+        //~^ ERROR unused variable: `b`
+    | {};
+    let _ = a(1, 2);
+    let _ = b(1, 2);
+}
diff --git a/src/test/ui/lint/lint-unused-variables.stderr b/src/test/ui/lint/lint-unused-variables.stderr
new file mode 100644
index 00000000000..7ed5669e33c
--- /dev/null
+++ b/src/test/ui/lint/lint-unused-variables.stderr
@@ -0,0 +1,56 @@
+error: unused variable: `a`
+  --> $DIR/lint-unused-variables.rs:8:5
+   |
+LL |     a: i32,
+   |     ^ help: consider prefixing with an underscore: `_a`
+   |
+note: lint level defined here
+  --> $DIR/lint-unused-variables.rs:5:9
+   |
+LL | #![deny(unused_variables)]
+   |         ^^^^^^^^^^^^^^^^
+
+error: unused variable: `b`
+  --> $DIR/lint-unused-variables.rs:14:5
+   |
+LL |     b: i32,
+   |     ^ help: consider prefixing with an underscore: `_b`
+
+error: unused variable: `a`
+  --> $DIR/lint-unused-variables.rs:53:9
+   |
+LL |         a: i32,
+   |         ^ help: consider prefixing with an underscore: `_a`
+
+error: unused variable: `b`
+  --> $DIR/lint-unused-variables.rs:59:9
+   |
+LL |         b: i32,
+   |         ^ help: consider prefixing with an underscore: `_b`
+
+error: unused variable: `b`
+  --> $DIR/lint-unused-variables.rs:37:9
+   |
+LL |         b: i32,
+   |         ^ help: consider prefixing with an underscore: `_b`
+
+error: unused variable: `a`
+  --> $DIR/lint-unused-variables.rs:22:9
+   |
+LL |         a: i32,
+   |         ^ help: consider prefixing with an underscore: `_a`
+
+error: unused variable: `b`
+  --> $DIR/lint-unused-variables.rs:29:9
+   |
+LL |         b: i32,
+   |         ^ help: consider prefixing with an underscore: `_b`
+
+error: unused variable: `b`
+  --> $DIR/lint-unused-variables.rs:45:9
+   |
+LL |         b: i32,
+   |         ^ help: consider prefixing with an underscore: `_b`
+
+error: aborting due to 8 previous errors
+
diff --git a/src/test/ui/mismatched_types/issue-38371.stderr b/src/test/ui/mismatched_types/issue-38371.stderr
index a9347926bda..79a0807c337 100644
--- a/src/test/ui/mismatched_types/issue-38371.stderr
+++ b/src/test/ui/mismatched_types/issue-38371.stderr
@@ -2,11 +2,13 @@ error[E0308]: mismatched types
   --> $DIR/issue-38371.rs:4:8
    |
 LL | fn foo(&foo: Foo) {
-   |        ^^^^ expected struct `Foo`, found reference
+   |        ^^^^------
+   |        |
+   |        expected struct `Foo`, found reference
+   |        help: did you mean `foo`: `&Foo`
    |
    = note: expected type `Foo`
               found type `&_`
-   = help: did you mean `foo: &Foo`?
 
 error[E0308]: mismatched types
   --> $DIR/issue-38371.rs:18:9
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
index e796e37bbaa..5eeda66173d 100644
--- a/src/test/ui/rfc-2565-param-attrs/param-attrs-allowed.rs
+++ b/src/test/ui/rfc-2565-param-attrs/param-attrs-allowed.rs
@@ -1,189 +1,66 @@
+// check-pass
 // compile-flags: --cfg something
-// build-pass (FIXME(62277): could be check-pass?)
 
+#![deny(unused_mut)]
 #![feature(param_attrs)]
 
 extern "C" {
     fn ffi(
-        #[allow(C)] a: i32,
+        #[allow(unused_mut)] a: i32,
         #[cfg(something)] b: i32,
         #[cfg_attr(something, cfg(nothing))] c: i32,
-        #[deny(C)] d: i32,
-        #[forbid(C)] #[warn(C)] ...
+        #[deny(unused_mut)] d: i32,
+        #[forbid(unused_mut)] #[warn(unused_mut)] ...
     );
 }
 
 type FnType = fn(
-    #[allow(C)] a: i32,
+    #[allow(unused_mut)] a: i32,
     #[cfg(something)] b: i32,
     #[cfg_attr(something, cfg(nothing))] c: i32,
-    #[deny(C)] d: i32,
-    #[forbid(C)] #[warn(C)] e: i32
+    #[deny(unused_mut)] d: i32,
+    #[forbid(unused_mut)] #[warn(unused_mut)] e: i32
 );
 
 pub fn foo(
-    #[allow(C)] a: i32,
+    #[allow(unused_mut)] a: i32,
     #[cfg(something)] b: i32,
     #[cfg_attr(something, cfg(nothing))] c: i32,
-    #[deny(C)] d: i32,
-    #[forbid(C)] #[warn(C)] e: i32
+    #[deny(unused_mut)] d: i32,
+    #[forbid(unused_mut)] #[warn(unused_mut)] _e: i32
 ) {}
 
-// self, &self and &mut self
+// self
 
 struct SelfStruct {}
 impl SelfStruct {
     fn foo(
-        #[allow(C)] self,
+        #[allow(unused_mut)] self,
         #[cfg(something)] a: i32,
         #[cfg_attr(something, cfg(nothing))]
-        #[deny(C)] b: i32,
+        #[deny(unused_mut)] b: i32,
     ) {}
 }
 
 struct RefStruct {}
 impl RefStruct {
     fn foo(
-        #[allow(C)] &self,
+        #[allow(unused_mut)] &self,
         #[cfg(something)] a: i32,
         #[cfg_attr(something, cfg(nothing))]
-        #[deny(C)] b: i32,
+        #[deny(unused_mut)] b: i32,
     ) {}
 }
 trait RefTrait {
     fn foo(
-        #[forbid(C)] &self,
-        #[warn(C)] a: i32
+        #[forbid(unused_mut)] &self,
+        #[warn(unused_mut)] 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
+        #[forbid(unused_mut)] &self,
+        #[warn(unused_mut)] a: i32
     ) {}
 }
 
@@ -192,22 +69,22 @@ impl NamedLifetimeMutTrait for NamedLifetimeMutStruct {
 struct BoxSelfStruct {}
 impl BoxSelfStruct {
     fn foo(
-        #[allow(C)] self: Box<Self>,
+        #[allow(unused_mut)] self: Box<Self>,
         #[cfg(something)] a: i32,
         #[cfg_attr(something, cfg(nothing))]
-        #[deny(C)] b: i32,
+        #[deny(unused_mut)] b: i32,
     ) {}
 }
 trait BoxSelfTrait {
     fn foo(
-        #[forbid(C)] self: Box<Self>,
-        #[warn(C)] a: i32
+        #[forbid(unused_mut)] self: Box<Self>,
+        #[warn(unused_mut)] a: i32
     ) {}
 }
 impl BoxSelfTrait for BoxSelfStruct {
     fn foo(
-        #[forbid(C)] self: Box<Self>,
-        #[warn(C)] a: i32
+        #[forbid(unused_mut)] self: Box<Self>,
+        #[warn(unused_mut)] a: i32
     ) {}
 }
 
@@ -216,10 +93,10 @@ fn main() {
     let _: fn(_, _, _, _) = foo;
     let _: FnType = |_, _, _, _| {};
     let c = |
-        #[allow(C)] a: u32,
+        #[allow(unused_mut)] a: u32,
         #[cfg(something)] b: i32,
         #[cfg_attr(something, cfg(nothing))]
-        #[deny(C)] c: i32,
+        #[deny(unused_mut)] c: i32,
     | {};
     let _ = c(1, 2);
 }
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
index 977b5d9ce34..069332ffa25 100644
--- a/src/test/ui/rfc-2565-param-attrs/param-attrs-cfg.rs
+++ b/src/test/ui/rfc-2565-param-attrs/param-attrs-cfg.rs
@@ -1,6 +1,7 @@
 // compile-flags: --cfg something
+// edition:2018
 
-#![feature(param_attrs)]
+#![feature(async_await, async_closure, param_attrs)]
 #![deny(unused_variables)]
 
 extern "C" {
@@ -19,24 +20,35 @@ type FnType = fn(
     #[cfg_attr(something, cfg(nothing))] d: i32,
 );
 
+async fn foo_async(
+    #[cfg(something)] a: i32,
+    //~^ ERROR unused variable: `a`
+    #[cfg(nothing)] b: i32,
+) {}
 fn foo(
     #[cfg(nothing)] a: i32,
     #[cfg(something)] b: i32,
-    //~^ ERROR unused variable: `b` [unused_variables]
+    //~^ ERROR unused variable: `b`
     #[cfg_attr(nothing, cfg(nothing))] c: i32,
-    //~^ ERROR unused variable: `c` [unused_variables]
+    //~^ ERROR unused variable: `c`
     #[cfg_attr(something, cfg(nothing))] d: i32,
 ) {}
 
 struct RefStruct {}
 impl RefStruct {
+    async fn bar_async(
+        &self,
+        #[cfg(something)] a: i32,
+        //~^ ERROR unused variable: `a`
+        #[cfg(nothing)] b: i32,
+    ) {}
     fn bar(
         &self,
         #[cfg(nothing)] a: i32,
         #[cfg(something)] b: i32,
-        //~^ ERROR unused variable: `b` [unused_variables]
+        //~^ ERROR unused variable: `b`
         #[cfg_attr(nothing, cfg(nothing))] c: i32,
-        //~^ ERROR unused variable: `c` [unused_variables]
+        //~^ ERROR unused variable: `c`
         #[cfg_attr(something, cfg(nothing))] d: i32,
     ) {}
 }
@@ -45,9 +57,9 @@ trait RefTrait {
         &self,
         #[cfg(nothing)] a: i32,
         #[cfg(something)] b: i32,
-        //~^ ERROR unused variable: `b` [unused_variables]
+        //~^ ERROR unused variable: `b`
         #[cfg_attr(nothing, cfg(nothing))] c: i32,
-        //~^ ERROR unused variable: `c` [unused_variables]
+        //~^ ERROR unused variable: `c`
         #[cfg_attr(something, cfg(nothing))] d: i32,
     ) {}
 }
@@ -56,9 +68,9 @@ impl RefTrait for RefStruct {
         &self,
         #[cfg(nothing)] a: i32,
         #[cfg(something)] b: i32,
-        //~^ ERROR unused variable: `b` [unused_variables]
+        //~^ ERROR unused variable: `b`
         #[cfg_attr(nothing, cfg(nothing))] c: i32,
-        //~^ ERROR unused variable: `c` [unused_variables]
+        //~^ ERROR unused variable: `c`
         #[cfg_attr(something, cfg(nothing))] d: i32,
     ) {}
 }
@@ -67,13 +79,19 @@ fn main() {
     let _: unsafe extern "C" fn(_, ...) = ffi;
     let _: fn(_, _) = foo;
     let _: FnType = |_, _| {};
+    let a = async move |
+        #[cfg(something)] a: i32,
+        //~^ ERROR unused variable: `a`
+        #[cfg(nothing)] b: i32,
+    | {};
     let c = |
         #[cfg(nothing)] a: i32,
         #[cfg(something)] b: i32,
-        //~^ ERROR unused variable: `b` [unused_variables]
+        //~^ ERROR unused variable: `b`
         #[cfg_attr(nothing, cfg(nothing))] c: i32,
-        //~^ ERROR unused variable: `c` [unused_variables]
+        //~^ ERROR unused variable: `c`
         #[cfg_attr(something, cfg(nothing))] d: i32,
     | {};
+    let _ = a(1);
     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
index c97190324e5..3232e2a0411 100644
--- a/src/test/ui/rfc-2565-param-attrs/param-attrs-cfg.stderr
+++ b/src/test/ui/rfc-2565-param-attrs/param-attrs-cfg.stderr
@@ -1,68 +1,86 @@
-error: unused variable: `b`
+error: unused variable: `a`
   --> $DIR/param-attrs-cfg.rs:24:23
    |
-LL |     #[cfg(something)] b: i32,
-   |                       ^ help: consider prefixing with an underscore: `_b`
+LL |     #[cfg(something)] a: i32,
+   |                       ^ help: consider prefixing with an underscore: `_a`
    |
 note: lint level defined here
-  --> $DIR/param-attrs-cfg.rs:4:9
+  --> $DIR/param-attrs-cfg.rs:5:9
    |
 LL | #![deny(unused_variables)]
    |         ^^^^^^^^^^^^^^^^
 
+error: unused variable: `b`
+  --> $DIR/param-attrs-cfg.rs:30:23
+   |
+LL |     #[cfg(something)] b: i32,
+   |                       ^ help: consider prefixing with an underscore: `_b`
+
 error: unused variable: `c`
-  --> $DIR/param-attrs-cfg.rs:26:40
+  --> $DIR/param-attrs-cfg.rs:32:40
    |
 LL |     #[cfg_attr(nothing, cfg(nothing))] c: i32,
    |                                        ^ help: consider prefixing with an underscore: `_c`
 
+error: unused variable: `a`
+  --> $DIR/param-attrs-cfg.rs:83:27
+   |
+LL |         #[cfg(something)] a: i32,
+   |                           ^ help: consider prefixing with an underscore: `_a`
+
 error: unused variable: `b`
-  --> $DIR/param-attrs-cfg.rs:72:27
+  --> $DIR/param-attrs-cfg.rs:89:27
    |
 LL |         #[cfg(something)] b: i32,
    |                           ^ help: consider prefixing with an underscore: `_b`
 
 error: unused variable: `c`
-  --> $DIR/param-attrs-cfg.rs:74:44
+  --> $DIR/param-attrs-cfg.rs:91: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
+  --> $DIR/param-attrs-cfg.rs:59:27
    |
 LL |         #[cfg(something)] b: i32,
    |                           ^ help: consider prefixing with an underscore: `_b`
 
 error: unused variable: `c`
-  --> $DIR/param-attrs-cfg.rs:49:44
+  --> $DIR/param-attrs-cfg.rs:61:44
    |
 LL |         #[cfg_attr(nothing, cfg(nothing))] c: i32,
    |                                            ^ help: consider prefixing with an underscore: `_c`
 
+error: unused variable: `a`
+  --> $DIR/param-attrs-cfg.rs:41:27
+   |
+LL |         #[cfg(something)] a: i32,
+   |                           ^ help: consider prefixing with an underscore: `_a`
+
 error: unused variable: `b`
-  --> $DIR/param-attrs-cfg.rs:36:27
+  --> $DIR/param-attrs-cfg.rs:48:27
    |
 LL |         #[cfg(something)] b: i32,
    |                           ^ help: consider prefixing with an underscore: `_b`
 
 error: unused variable: `c`
-  --> $DIR/param-attrs-cfg.rs:38:44
+  --> $DIR/param-attrs-cfg.rs:50: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
+  --> $DIR/param-attrs-cfg.rs:70:27
    |
 LL |         #[cfg(something)] b: i32,
    |                           ^ help: consider prefixing with an underscore: `_b`
 
 error: unused variable: `c`
-  --> $DIR/param-attrs-cfg.rs:60:44
+  --> $DIR/param-attrs-cfg.rs:72:44
    |
 LL |         #[cfg_attr(nothing, cfg(nothing))] c: i32,
    |                                            ^ help: consider prefixing with an underscore: `_c`
 
-error: aborting due to 10 previous errors
+error: aborting due to 13 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
index c5a6514efb0..a7f4855915b 100644
--- 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
@@ -1,12 +1,14 @@
 // gate-test-param_attrs
 
+#![deny(unused_variables)]
+
 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
+    #[allow(unused_variables)] a: u8
     //~^ ERROR attributes on function parameters are unstable
     //~| NOTE https://github.com/rust-lang/rust/issues/60406
 ) {}
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
index 704c41f0fa6..0bb9d05dca0 100644
--- 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
@@ -1,11 +1,11 @@
 error: documentation comments cannot be applied to function parameters
-  --> $DIR/param-attrs-feature-gate.rs:4:5
+  --> $DIR/param-attrs-feature-gate.rs:6: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
+  --> $DIR/param-attrs-feature-gate.rs:6:5
    |
 LL |     /// Foo
    |     ^^^^^^^
@@ -14,10 +14,10 @@ LL |     /// Foo
    = 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
+  --> $DIR/param-attrs-feature-gate.rs:11:5
    |
-LL |     #[allow(C)] a: u8
-   |     ^^^^^^^^^^^
+LL |     #[allow(unused_variables)] 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