From b651c1cebe5f365201d568507ddb49ca343f2e7d Mon Sep 17 00:00:00 2001
From: Eric Huss <eric@huss.org>
Date: Tue, 3 May 2022 13:23:03 -0700
Subject: [PATCH] Check attributes on struct expression fields.

Attributes on struct expression fields were not being checked for
validity. This adds the fields as HIR nodes so that `CheckAttrVisitor`
can visit those nodes to check their attributes.
---
 compiler/rustc_ast_lowering/src/expr.rs       |  4 ++-
 compiler/rustc_ast_lowering/src/index.rs      |  5 ++++
 compiler/rustc_hir/src/hir.rs                 |  2 ++
 compiler/rustc_hir/src/target.rs              |  2 ++
 compiler/rustc_hir_pretty/src/lib.rs          | 30 ++++++++++---------
 compiler/rustc_middle/src/hir/map/mod.rs      |  3 ++
 compiler/rustc_passes/src/check_attr.rs       |  8 ++++-
 .../drop_ranges/cfg_build.rs                  |  1 +
 .../lint/unused/unused_attributes-must_use.rs |  2 +-
 .../unused/unused_attributes-must_use.stderr  |  8 ++++-
 10 files changed, 47 insertions(+), 18 deletions(-)

diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 92e6bc6013d..32dbd2ff47d 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -1406,8 +1406,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
     }
 
     fn lower_expr_field(&mut self, f: &ExprField) -> hir::ExprField<'hir> {
+        let hir_id = self.lower_node_id(f.id);
+        self.lower_attrs(hir_id, &f.attrs);
         hir::ExprField {
-            hir_id: self.next_id(),
+            hir_id,
             ident: self.lower_ident(f.ident),
             expr: self.lower_expr(&f.expr),
             span: self.lower_span(f.span),
diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs
index ecc26faf20d..ea35cf3de04 100644
--- a/compiler/rustc_ast_lowering/src/index.rs
+++ b/compiler/rustc_ast_lowering/src/index.rs
@@ -224,6 +224,11 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
 
     fn visit_expr(&mut self, expr: &'hir Expr<'hir>) {
         self.insert(expr.span, expr.hir_id, Node::Expr(expr));
+        if let ExprKind::Struct(_, fields, _) = expr.kind {
+            for field in fields {
+                self.insert(field.span, field.hir_id, Node::ExprField(field));
+            }
+        }
 
         self.with_parent(expr.hir_id, |this| {
             intravisit::walk_expr(this, expr);
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 9885d33d444..2610d0b92d8 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -3332,6 +3332,7 @@ pub enum Node<'hir> {
     Field(&'hir FieldDef<'hir>),
     AnonConst(&'hir AnonConst),
     Expr(&'hir Expr<'hir>),
+    ExprField(&'hir ExprField<'hir>),
     Stmt(&'hir Stmt<'hir>),
     PathSegment(&'hir PathSegment<'hir>),
     Ty(&'hir Ty<'hir>),
@@ -3390,6 +3391,7 @@ impl<'hir> Node<'hir> {
             | Node::Ctor(..)
             | Node::Pat(..)
             | Node::PatField(..)
+            | Node::ExprField(..)
             | Node::Arm(..)
             | Node::Local(..)
             | Node::Crate(..)
diff --git a/compiler/rustc_hir/src/target.rs b/compiler/rustc_hir/src/target.rs
index 1b05c82eade..78bfd7191db 100644
--- a/compiler/rustc_hir/src/target.rs
+++ b/compiler/rustc_hir/src/target.rs
@@ -57,6 +57,7 @@ pub enum Target {
     MacroDef,
     Param,
     PatField,
+    ExprField,
 }
 
 impl Display for Target {
@@ -185,6 +186,7 @@ impl Target {
             Target::MacroDef => "macro def",
             Target::Param => "function param",
             Target::PatField => "pattern field",
+            Target::ExprField => "struct field",
         }
     }
 }
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 641175d4529..fde073296f9 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -83,6 +83,7 @@ impl<'a> State<'a> {
             Node::Variant(a) => self.print_variant(a),
             Node::AnonConst(a) => self.print_anon_const(a),
             Node::Expr(a) => self.print_expr(a),
+            Node::ExprField(a) => self.print_expr_field(&a),
             Node::Stmt(a) => self.print_stmt(a),
             Node::PathSegment(a) => self.print_path_segment(a),
             Node::Ty(a) => self.print_type(a),
@@ -1124,20 +1125,7 @@ impl<'a> State<'a> {
     ) {
         self.print_qpath(qpath, true);
         self.word("{");
-        self.commasep_cmnt(
-            Consistent,
-            fields,
-            |s, field| {
-                s.ibox(INDENT_UNIT);
-                if !field.is_shorthand {
-                    s.print_ident(field.ident);
-                    s.word_space(":");
-                }
-                s.print_expr(field.expr);
-                s.end()
-            },
-            |f| f.span,
-        );
+        self.commasep_cmnt(Consistent, fields, |s, field| s.print_expr_field(field), |f| f.span);
         if let Some(expr) = wth {
             self.ibox(INDENT_UNIT);
             if !fields.is_empty() {
@@ -1154,6 +1142,20 @@ impl<'a> State<'a> {
         self.word("}");
     }
 
+    fn print_expr_field(&mut self, field: &hir::ExprField<'_>) {
+        if self.attrs(field.hir_id).is_empty() {
+            self.space();
+        }
+        self.cbox(INDENT_UNIT);
+        self.print_outer_attributes(&self.attrs(field.hir_id));
+        if !field.is_shorthand {
+            self.print_ident(field.ident);
+            self.word_space(":");
+        }
+        self.print_expr(&field.expr);
+        self.end()
+    }
+
     fn print_expr_tup(&mut self, exprs: &[hir::Expr<'_>]) {
         self.popen();
         self.commasep_exprs(Inconsistent, exprs);
diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs
index 06fdef4142e..79e6804a289 100644
--- a/compiler/rustc_middle/src/hir/map/mod.rs
+++ b/compiler/rustc_middle/src/hir/map/mod.rs
@@ -298,6 +298,7 @@ impl<'hir> Map<'hir> {
             | Node::TraitRef(_)
             | Node::Pat(_)
             | Node::PatField(_)
+            | Node::ExprField(_)
             | Node::Local(_)
             | Node::Param(_)
             | Node::Arm(_)
@@ -1021,6 +1022,7 @@ impl<'hir> Map<'hir> {
             Node::Field(field) => field.span,
             Node::AnonConst(constant) => self.body(constant.body).value.span,
             Node::Expr(expr) => expr.span,
+            Node::ExprField(field) => field.span,
             Node::Stmt(stmt) => stmt.span,
             Node::PathSegment(seg) => {
                 let ident_span = seg.ident.span;
@@ -1243,6 +1245,7 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String {
         }
         Some(Node::AnonConst(_)) => node_str("const"),
         Some(Node::Expr(_)) => node_str("expr"),
+        Some(Node::ExprField(_)) => node_str("expr field"),
         Some(Node::Stmt(_)) => node_str("stmt"),
         Some(Node::PathSegment(_)) => node_str("path segment"),
         Some(Node::Ty(_)) => node_str("type"),
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 42f5806c1f7..0eb6f401b38 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -653,7 +653,8 @@ impl CheckAttrVisitor<'_> {
             | Target::ForeignTy
             | Target::GenericParam(..)
             | Target::MacroDef
-            | Target::PatField => None,
+            | Target::PatField
+            | Target::ExprField => None,
         } {
             tcx.sess.emit_err(errors::DocAliasBadLocation { span, attr_str, location });
             return false;
@@ -2064,6 +2065,11 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
         };
 
         self.check_attributes(expr.hir_id, expr.span, target, None);
+        if let hir::ExprKind::Struct(_, fields, _) = expr.kind {
+            for field in fields {
+                self.check_attributes(field.hir_id, field.span, Target::PatField, None);
+            }
+        }
         intravisit::walk_expr(self, expr)
     }
 
diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs
index f1f4b05b33b..3e96b3ffb09 100644
--- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs
+++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs
@@ -257,6 +257,7 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
                 | hir::Node::TraitRef(..)
                 | hir::Node::Pat(..)
                 | hir::Node::PatField(..)
+                | hir::Node::ExprField(..)
                 | hir::Node::Arm(..)
                 | hir::Node::Local(..)
                 | hir::Node::Ctor(..)
diff --git a/src/test/ui/lint/unused/unused_attributes-must_use.rs b/src/test/ui/lint/unused/unused_attributes-must_use.rs
index 87f498c0aee..51f868706b6 100644
--- a/src/test/ui/lint/unused/unused_attributes-must_use.rs
+++ b/src/test/ui/lint/unused/unused_attributes-must_use.rs
@@ -126,6 +126,6 @@ fn main() {
     struct PatternField {
         foo: i32,
     }
-    let s = PatternField { foo: 123 };
+    let s = PatternField { #[must_use]  foo: 123 }; //~ ERROR `#[must_use]` has no effect
     let PatternField { #[must_use] foo } = s; //~ ERROR `#[must_use]` has no effect
 }
diff --git a/src/test/ui/lint/unused/unused_attributes-must_use.stderr b/src/test/ui/lint/unused/unused_attributes-must_use.stderr
index 3d2672687f8..69cd5302c5c 100644
--- a/src/test/ui/lint/unused/unused_attributes-must_use.stderr
+++ b/src/test/ui/lint/unused/unused_attributes-must_use.stderr
@@ -105,6 +105,12 @@ error: `#[must_use]` has no effect when applied to an match arm
 LL |         #[must_use]
    |         ^^^^^^^^^^^
 
+error: `#[must_use]` has no effect when applied to a pattern field
+  --> $DIR/unused_attributes-must_use.rs:129:28
+   |
+LL |     let s = PatternField { #[must_use]  foo: 123 };
+   |                            ^^^^^^^^^^^
+
 error: `#[must_use]` has no effect when applied to a pattern field
   --> $DIR/unused_attributes-must_use.rs:130:24
    |
@@ -177,5 +183,5 @@ error: unused return value of `Use::get_four` that must be used
 LL |     ().get_four();
    |     ^^^^^^^^^^^^^^
 
-error: aborting due to 27 previous errors
+error: aborting due to 28 previous errors