about summary refs log tree commit diff
diff options
context:
space:
mode:
authormatthew <matthew.tejo@gmail.com>2018-03-22 08:57:26 -0700
committermatthew <matthew.tejo@gmail.com>2018-03-26 08:43:16 -0700
commit816c1b191cbea4f4949bc584c2324e81caa6e34b (patch)
tree3ca91ee3dd8660adb0221e89ce03c2d624ec3e44
parentc08480fce0f39f5c9c6db6dde0dccb375ca0ab14 (diff)
downloadrust-816c1b191cbea4f4949bc584c2324e81caa6e34b.tar.gz
rust-816c1b191cbea4f4949bc584c2324e81caa6e34b.zip
Check for known but incorrect attributes
- Change nested_visit_map so it will recusively check functions

- Add visit_stmt and visit_expr for impl Visitor for CheckAttrVisitor and check for incorrect
inline and repr attributes on staements and expressions

- Add regression test for isssue #43988
-rw-r--r--src/librustc/hir/check_attr.rs91
-rw-r--r--src/test/compile-fail/issue-43988.rs36
2 files changed, 118 insertions, 9 deletions
diff --git a/src/librustc/hir/check_attr.rs b/src/librustc/hir/check_attr.rs
index 9b2647ad4db..6e1b7dc86a2 100644
--- a/src/librustc/hir/check_attr.rs
+++ b/src/librustc/hir/check_attr.rs
@@ -14,6 +14,7 @@
 //! conflicts between multiple such attributes attached to the same
 //! item.
 
+use syntax_pos::Span;
 use ty::TyCtxt;
 
 use hir;
@@ -27,6 +28,8 @@ enum Target {
     Enum,
     Const,
     ForeignMod,
+    Expression,
+    Statement,
     Other,
 }
 
@@ -62,7 +65,7 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
         let mut has_wasm_import_module = false;
         for attr in &item.attrs {
             if attr.check_name("inline") {
-                self.check_inline(attr, item, target)
+                self.check_inline(attr, &item.span, target)
             } else if attr.check_name("wasm_import_module") {
                 has_wasm_import_module = true;
                 if attr.value_str().is_none() {
@@ -99,13 +102,13 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
     }
 
     /// Check if an `#[inline]` is applied to a function.
-    fn check_inline(&self, attr: &hir::Attribute, item: &hir::Item, target: Target) {
+    fn check_inline(&self, attr: &hir::Attribute, span: &Span, target: Target) {
         if target != Target::Fn {
             struct_span_err!(self.tcx.sess,
                              attr.span,
                              E0518,
                              "attribute should be applied to function")
-                .span_label(item.span, "not a function")
+                .span_label(*span, "not a function")
                 .emit();
         }
     }
@@ -196,10 +199,12 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
                 }
                 _ => continue,
             };
-            struct_span_err!(self.tcx.sess, hint.span, E0517,
-                             "attribute should be applied to {}", allowed_targets)
-                .span_label(item.span, format!("not {} {}", article, allowed_targets))
-                .emit();
+            self.emit_repr_error(
+                hint.span,
+                item.span,
+                &format!("attribute should be applied to {}", allowed_targets),
+                &format!("not {} {}", article, allowed_targets),
+            )
         }
 
         // Just point at all repr hints if there are any incompatibilities.
@@ -221,17 +226,85 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
                        "conflicting representation hints");
         }
     }
+
+    fn emit_repr_error(
+        &self,
+        hint_span: Span,
+        label_span: Span,
+        hint_message: &str,
+        label_message: &str,
+    ) {
+        struct_span_err!(self.tcx.sess, hint_span, E0517, "{}", hint_message)
+            .span_label(label_span, label_message)
+            .emit();
+    }
+
+    fn check_stmt_attributes(&self, stmt: &hir::Stmt) {
+        // When checking statements ignore expressions, they will be checked later
+        if let hir::Stmt_::StmtDecl(_, _) = stmt.node {
+            for attr in stmt.node.attrs() {
+                if attr.check_name("inline") {
+                    self.check_inline(attr, &stmt.span, Target::Statement);
+                }
+                if attr.check_name("repr") {
+                    self.emit_repr_error(
+                        attr.span,
+                        stmt.span,
+                        &format!("attribute should not be applied to statements"),
+                        &format!("not a struct, enum or union"),
+                    );
+                }
+            }
+        }
+    }
+
+    fn check_expr_attributes(&self, expr: &hir::Expr) {
+        use hir::Expr_::*;
+        match expr.node {
+            // Assignments, Calls and Structs were handled by Items and Statements
+            ExprCall(..) |
+            ExprAssign(..) |
+            ExprMethodCall(..) |
+            ExprStruct(..) => return,
+            _ => (),
+        }
+
+        for attr in expr.attrs.iter() {
+            if attr.check_name("inline") {
+                self.check_inline(attr, &expr.span, Target::Expression);
+            }
+            if attr.check_name("repr") {
+                self.emit_repr_error(
+                    attr.span,
+                    expr.span,
+                    &format!("attribute should not be applied to an expression"),
+                    &format!("not a struct, enum or union"),
+                );
+            }
+        }
+    }
 }
 
 impl<'a, 'tcx> Visitor<'tcx> for CheckAttrVisitor<'a, 'tcx> {
     fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
-        NestedVisitorMap::None
+        NestedVisitorMap::OnlyBodies(&self.tcx.hir)
     }
 
     fn visit_item(&mut self, item: &'tcx hir::Item) {
         let target = Target::from_item(item);
         self.check_attributes(item, target);
-        intravisit::walk_item(self, item);
+        intravisit::walk_item(self, item)
+    }
+
+
+    fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt) {
+        self.check_stmt_attributes(stmt);
+        intravisit::walk_stmt(self, stmt)
+    }
+
+    fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
+        self.check_expr_attributes(expr);
+        intravisit::walk_expr(self, expr)
     }
 }
 
diff --git a/src/test/compile-fail/issue-43988.rs b/src/test/compile-fail/issue-43988.rs
new file mode 100644
index 00000000000..78237e31ba0
--- /dev/null
+++ b/src/test/compile-fail/issue-43988.rs
@@ -0,0 +1,36 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+
+    #[inline]
+    let _a = 4;
+    //~^^ ERROR attribute should be applied to function
+
+
+    #[inline(XYZ)]
+    let _b = 4;
+    //~^^ ERROR attribute should be applied to function
+
+    #[repr(nothing)]
+    let _x = 0;
+    //~^^ ERROR attribute should not be applied to statements
+
+
+    #[repr(something_not_real)]
+    loop {
+        ()
+    };
+    //~^^^^ ERROR attribute should not be applied to an expression
+
+    #[repr]
+    let _y = "123";
+    //~^^ ERROR attribute should not be applied to statements
+}