about summary refs log tree commit diff
path: root/compiler/rustc_passes/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-09-12 22:04:37 +0000
committerbors <bors@rust-lang.org>2020-09-12 22:04:37 +0000
commitdbb73f8f79ab176a897d5a95e696adb71b957cbe (patch)
tree4c73e2b5da6f978072701bb4563c21ba67ad17bd /compiler/rustc_passes/src
parent989190874fe2a0e9877ce4f02a6c60641e3d42a3 (diff)
parent82cb37911124a608986961f819b60291965ead3f (diff)
downloadrust-dbb73f8f79ab176a897d5a95e696adb71b957cbe.tar.gz
rust-dbb73f8f79ab176a897d5a95e696adb71b957cbe.zip
Auto merge of #73461 - calebzulawski:validate-attribute-placement, r=matthewjasper
Validate built-in attribute placement

Closes #54584, closes #47725, closes #54044.

I've changed silently ignoring some incorrectly placed attributes to errors.  I'm not sure what the policy is since this can theoretically break code (should they be warnings instead? does it warrant a crater run?).
Diffstat (limited to 'compiler/rustc_passes/src')
-rw-r--r--compiler/rustc_passes/src/check_attr.rs197
1 files changed, 183 insertions, 14 deletions
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 392070839dc..efe947daa28 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -66,12 +66,26 @@ impl CheckAttrVisitor<'tcx> {
             } else if self.tcx.sess.check_name(attr, sym::marker) {
                 self.check_marker(attr, span, target)
             } else if self.tcx.sess.check_name(attr, sym::target_feature) {
-                self.check_target_feature(attr, span, target)
+                self.check_target_feature(hir_id, attr, span, target)
             } else if self.tcx.sess.check_name(attr, sym::track_caller) {
                 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::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 {
+                // lint-only checks
+                if self.tcx.sess.check_name(attr, sym::cold) {
+                    self.check_cold(hir_id, attr, span, target);
+                } else if self.tcx.sess.check_name(attr, sym::link_name) {
+                    self.check_link_name(hir_id, attr, span, target);
+                } else if self.tcx.sess.check_name(attr, sym::link_section) {
+                    self.check_link_section(hir_id, attr, span, target);
+                } else if self.tcx.sess.check_name(attr, sym::no_mangle) {
+                    self.check_no_mangle(hir_id, attr, span, target);
+                }
                 true
             };
         }
@@ -109,12 +123,12 @@ impl CheckAttrVisitor<'tcx> {
                     lint.build("`#[inline]` is ignored on constants")
                         .warn(
                             "this was previously accepted by the compiler but is \
-                               being phased out; it will become a hard error in \
-                               a future release!",
+                             being phased out; it will become a hard error in \
+                             a future release!",
                         )
                         .note(
                             "see issue #65833 <https://github.com/rust-lang/rust/issues/65833> \
-                                 for more information",
+                             for more information",
                         )
                         .emit();
                 });
@@ -153,7 +167,7 @@ impl CheckAttrVisitor<'tcx> {
                 .emit();
                 false
             }
-            Target::Fn | Target::Method(..) | Target::ForeignFn => true,
+            Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => true,
             _ => {
                 struct_span_err!(
                     self.tcx.sess,
@@ -202,10 +216,31 @@ impl CheckAttrVisitor<'tcx> {
     }
 
     /// Checks if the `#[target_feature]` attribute on `item` is valid. Returns `true` if valid.
-    fn check_target_feature(&self, attr: &Attribute, span: &Span, target: Target) -> bool {
+    fn check_target_feature(
+        &self,
+        hir_id: HirId,
+        attr: &Attribute,
+        span: &Span,
+        target: Target,
+    ) -> bool {
         match target {
             Target::Fn
             | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
+            // FIXME: #[target_feature] was previously erroneously allowed on statements and some
+            // crates used this, so only emit a warning.
+            Target::Statement => {
+                self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
+                    lint.build("attribute should be applied to a function")
+                        .warn(
+                            "this was previously accepted by the compiler but is \
+                             being phased out; it will become a hard error in \
+                             a future release!",
+                        )
+                        .span_label(*span, "not a function")
+                        .emit();
+                });
+                true
+            }
             _ => {
                 self.tcx
                     .sess
@@ -277,6 +312,136 @@ impl CheckAttrVisitor<'tcx> {
         true
     }
 
+    /// Checks if `#[cold]` is applied to a non-function. Returns `true` if valid.
+    fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
+        match target {
+            Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => {}
+            _ => {
+                // FIXME: #[cold] was previously allowed on non-functions and some crates used
+                // this, so only emit a warning.
+                self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
+                    lint.build("attribute should be applied to a function")
+                        .warn(
+                            "this was previously accepted by the compiler but is \
+                             being phased out; it will become a hard error in \
+                             a future release!",
+                        )
+                        .span_label(*span, "not a function")
+                        .emit();
+                });
+            }
+        }
+    }
+
+    /// Checks if `#[link_name]` is applied to an item other than a foreign function or static.
+    fn check_link_name(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
+        match target {
+            Target::ForeignFn | Target::ForeignStatic => {}
+            _ => {
+                // FIXME: #[cold] was previously allowed on non-functions/statics and some crates
+                // used this, so only emit a warning.
+                self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
+                    let mut diag =
+                        lint.build("attribute should be applied to a foreign function or static");
+                    diag.warn(
+                        "this was previously accepted by the compiler but is \
+                         being phased out; it will become a hard error in \
+                         a future release!",
+                    );
+
+                    // See issue #47725
+                    if let Target::ForeignMod = target {
+                        if let Some(value) = attr.value_str() {
+                            diag.span_help(
+                                attr.span,
+                                &format!(r#"try `#[link(name = "{}")]` instead"#, value),
+                            );
+                        } else {
+                            diag.span_help(attr.span, r#"try `#[link(name = "...")]` instead"#);
+                        }
+                    }
+
+                    diag.span_label(*span, "not a foreign function or static");
+                    diag.emit();
+                });
+            }
+        }
+    }
+
+    /// 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.
+    fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
+        match target {
+            Target::Static | Target::Fn | Target::Method(..) => {}
+            _ => {
+                // FIXME: #[link_section] was previously allowed on non-functions/statics and some
+                // crates used this, so only emit a warning.
+                self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
+                    lint.build("attribute should be applied to a function or static")
+                        .warn(
+                            "this was previously accepted by the compiler but is \
+                             being phased out; it will become a hard error in \
+                             a future release!",
+                        )
+                        .span_label(*span, "not a function or static")
+                        .emit();
+                });
+            }
+        }
+    }
+
+    /// Checks if `#[no_mangle]` is applied to a function or static.
+    fn check_no_mangle(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
+        match target {
+            Target::Static | Target::Fn | Target::Method(..) => {}
+            _ => {
+                // FIXME: #[no_mangle] was previously allowed on non-functions/statics and some
+                // crates used this, so only emit a warning.
+                self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
+                    lint.build("attribute should be applied to a function or static")
+                        .warn(
+                            "this was previously accepted by the compiler but is \
+                             being phased out; it will become a hard error in \
+                             a future release!",
+                        )
+                        .span_label(*span, "not a function or static")
+                        .emit();
+                });
+            }
+        }
+    }
+
     /// Checks if the `#[repr]` attributes on `item` are valid.
     fn check_repr(
         &self,
@@ -321,7 +486,11 @@ impl CheckAttrVisitor<'tcx> {
                 }
                 sym::simd => {
                     is_simd = true;
-                    if target != Target::Struct { ("a", "struct") } else { continue }
+                    if target != Target::Struct {
+                        ("a", "struct")
+                    } else {
+                        continue;
+                    }
                 }
                 sym::transparent => {
                     is_transparent = true;
@@ -358,7 +527,11 @@ impl CheckAttrVisitor<'tcx> {
                 | sym::isize
                 | sym::usize => {
                     int_reprs += 1;
-                    if target != Target::Enum { ("an", "enum") } else { continue }
+                    if target != Target::Enum {
+                        ("an", "enum")
+                    } else {
+                        continue;
+                    }
                 }
                 _ => continue,
             };
@@ -421,10 +594,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 +613,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,