about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorCaleb Zulawski <caleb.zulawski@gmail.com>2020-06-14 00:47:42 -0400
committerCaleb Zulawski <caleb.zulawski@gmail.com>2020-09-05 20:45:43 -0400
commit4efe97a3d9a5f2d295bc2fa9bd2bb90edf1986d5 (patch)
treeef7b2d81a2d2b0c2f467631d6585a038bd200e1c /compiler
parentde921ab3c3aa25d65b1476d77285da1ca99af397 (diff)
downloadrust-4efe97a3d9a5f2d295bc2fa9bd2bb90edf1986d5.tar.gz
rust-4efe97a3d9a5f2d295bc2fa9bd2bb90edf1986d5.zip
Check placement of more attributes
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_passes/src/check_attr.rs111
-rw-r--r--compiler/rustc_typeck/src/collect.rs30
2 files changed, 116 insertions, 25 deletions
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 392070839dc..037a653e3e0 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -71,6 +71,16 @@ impl CheckAttrVisitor<'tcx> {
                 self.check_track_caller(&attr.span, attrs, span, target)
             } else if self.tcx.sess.check_name(attr, sym::doc) {
                 self.check_doc_alias(attr, hir_id, target)
+            } else if self.tcx.sess.check_name(attr, sym::cold) {
+                self.check_cold(&attr, span, target)
+            } else if self.tcx.sess.check_name(attr, sym::link_name) {
+                self.check_link_name(&attr, span, target)
+            } else if self.tcx.sess.check_name(attr, sym::no_link) {
+                self.check_no_link(&attr, span, target)
+            } else if self.tcx.sess.check_name(attr, sym::export_name) {
+                self.check_export_name(&attr, span, target)
+            } else if self.tcx.sess.check_name(attr, sym::link_section) {
+                self.check_link_section(&attr, span, target)
             } else {
                 true
             };
@@ -277,6 +287,99 @@ impl CheckAttrVisitor<'tcx> {
         true
     }
 
+    /// Checks if `#[cold]` is applied to a non-function. Returns `true` if valid.
+    fn check_cold(&self, attr: &Attribute, span: &Span, target: Target) -> bool {
+        match target {
+            Target::Fn | Target::Method(..) | Target::ForeignFn => true,
+            _ => {
+                self.tcx
+                    .sess
+                    .struct_span_err(attr.span, "attribute should be applied to a function")
+                    .span_label(*span, "not a function")
+                    .emit();
+                false
+            }
+        }
+    }
+
+    /// Checks if `#[link_name]` is applied to an item other than a foreign function or static. Returns `true` if valid.
+    fn check_link_name(&self, attr: &Attribute, span: &Span, target: Target) -> bool {
+        if target == Target::ForeignFn || target == Target::ForeignStatic {
+            true
+        } else {
+            let mut err = self.tcx.sess.struct_span_err(
+                attr.span,
+                "attribute should be applied to a foreign function or static",
+            );
+            err.span_label(*span, "not a foreign function or static");
+
+            // See issue #47725
+            if target == Target::ForeignMod {
+                if let Some(value) = attr.value_str() {
+                    err.span_help(
+                        attr.span,
+                        &format!(r#"try `#[link(name = "{}")]` instead"#, value),
+                    );
+                } else {
+                    err.span_help(attr.span, r#"try `#[link(name = "...")]` instead"#);
+                }
+            }
+
+            err.emit();
+            false
+        }
+    }
+
+    /// Checks if `#[no_link]` is applied to an `extern crate`. Returns `true` if valid.
+    fn check_no_link(&self, attr: &Attribute, span: &Span, target: Target) -> bool {
+        if target == Target::ExternCrate {
+            true
+        } else {
+            self.tcx
+                .sess
+                .struct_span_err(attr.span, "attribute should be applied to an `extern crate` item")
+                .span_label(*span, "not an `extern crate` item")
+                .emit();
+            false
+        }
+    }
+
+    /// Checks if `#[export_name]` is applied to a function or static. Returns `true` if valid.
+    fn check_export_name(&self, attr: &Attribute, span: &Span, target: Target) -> bool {
+        match target {
+            Target::Static | Target::Fn | Target::Method(..) => true,
+            _ => {
+                self.tcx
+                    .sess
+                    .struct_span_err(
+                        attr.span,
+                        "attribute should be applied to a function or static",
+                    )
+                    .span_label(*span, "not a function or static")
+                    .emit();
+                false
+            }
+        }
+    }
+
+    /// Checks if `#[link_section]` is applied to a function or static. Returns `true` if valid.
+    fn check_link_section(&self, attr: &Attribute, span: &Span, target: Target) -> bool {
+        match target {
+            Target::Static | Target::Fn | Target::Method(..) => true,
+            _ => {
+                self.tcx
+                    .sess
+                    .struct_span_err(
+                        attr.span,
+                        "attribute should be applied to a function or static",
+                    )
+                    .span_label(*span, "not a function or static")
+                    .emit();
+                false
+            }
+        }
+    }
+
     /// Checks if the `#[repr]` attributes on `item` are valid.
     fn check_repr(
         &self,
@@ -421,10 +524,8 @@ impl CheckAttrVisitor<'tcx> {
     fn check_stmt_attributes(&self, stmt: &hir::Stmt<'_>) {
         // When checking statements ignore expressions, they will be checked later
         if let hir::StmtKind::Local(ref l) = stmt.kind {
+            self.check_attributes(l.hir_id, &l.attrs, &stmt.span, Target::Statement, None);
             for attr in l.attrs.iter() {
-                if self.tcx.sess.check_name(attr, sym::inline) {
-                    self.check_inline(l.hir_id, attr, &stmt.span, Target::Statement);
-                }
                 if self.tcx.sess.check_name(attr, sym::repr) {
                     self.emit_repr_error(
                         attr.span,
@@ -442,10 +543,8 @@ impl CheckAttrVisitor<'tcx> {
             hir::ExprKind::Closure(..) => Target::Closure,
             _ => Target::Expression,
         };
+        self.check_attributes(expr.hir_id, &expr.attrs, &expr.span, target, None);
         for attr in expr.attrs.iter() {
-            if self.tcx.sess.check_name(attr, sym::inline) {
-                self.check_inline(expr.hir_id, attr, &expr.span, target);
-            }
             if self.tcx.sess.check_name(attr, sym::repr) {
                 self.emit_repr_error(
                     attr.span,
diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs
index 94555e588bd..b316f724349 100644
--- a/compiler/rustc_typeck/src/collect.rs
+++ b/compiler/rustc_typeck/src/collect.rs
@@ -2490,10 +2490,17 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
                 codegen_fn_attrs.export_name = Some(s);
             }
         } else if tcx.sess.check_name(attr, sym::target_feature) {
-            if !tcx.features().target_feature_11 {
-                check_target_feature_safe_fn(tcx, id, attr.span);
-            } else if let Some(local_id) = id.as_local() {
-                if tcx.fn_sig(id).unsafety() == hir::Unsafety::Normal {
+            if !tcx.is_closure(id) && tcx.fn_sig(id).unsafety() == hir::Unsafety::Normal {
+                if !tcx.features().target_feature_11 {
+                    let mut err = feature_err(
+                        &tcx.sess.parse_sess,
+                        sym::target_feature_11,
+                        attr.span,
+                        "`#[target_feature(..)]` can only be applied to `unsafe` functions",
+                    );
+                    err.span_label(tcx.def_span(id), "not an `unsafe` function");
+                    err.emit();
+                } else if let Some(local_id) = id.as_local() {
                     check_target_feature_trait_unsafe(tcx, local_id, attr.span);
                 }
             }
@@ -2750,21 +2757,6 @@ fn check_link_name_xor_ordinal(
     }
 }
 
-/// Checks the function annotated with `#[target_feature]` is unsafe,
-/// reporting an error if it isn't.
-fn check_target_feature_safe_fn(tcx: TyCtxt<'_>, id: DefId, attr_span: Span) {
-    if tcx.is_closure(id) || tcx.fn_sig(id).unsafety() == hir::Unsafety::Normal {
-        let mut err = feature_err(
-            &tcx.sess.parse_sess,
-            sym::target_feature_11,
-            attr_span,
-            "`#[target_feature(..)]` can only be applied to `unsafe` functions",
-        );
-        err.span_label(tcx.def_span(id), "not an `unsafe` function");
-        err.emit();
-    }
-}
-
 /// Checks the function annotated with `#[target_feature]` is not a safe
 /// trait method implementation, reporting an error if it is.
 fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_span: Span) {