about summary refs log tree commit diff
path: root/compiler/rustc_parse/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-09-26 05:15:11 +0000
committerbors <bors@rust-lang.org>2021-09-26 05:15:11 +0000
commitf6e6ddc09d660fbfb4d483a9677d80768cc4e31c (patch)
tree61b3e7fda85f83fa96d221012da392502e7d1cf6 /compiler/rustc_parse/src
parent4da89a180facdecf168cbe0ddbc6bfbdd9f6e696 (diff)
parent75f058dbfd1c02daa191efb6eb53dc91fda8add6 (diff)
downloadrust-f6e6ddc09d660fbfb4d483a9677d80768cc4e31c.tar.gz
rust-f6e6ddc09d660fbfb4d483a9677d80768cc4e31c.zip
Auto merge of #88680 - ehuss:more-attr-validation, r=petrochenkov
Validate builtin attributes for macro args.

This adds some validation for `path`, `crate_type`, and `recursion_limit` attributes so that they will now return an error if you attempt to pass a macro into them (such as `#[path = foo!()]`). Previously, the attribute would be completely ignored. These attributes are special because their values need to be known before/during expansion.

cc #87681
Diffstat (limited to 'compiler/rustc_parse/src')
-rw-r--r--compiler/rustc_parse/src/validate_attr.rs133
1 files changed, 73 insertions, 60 deletions
diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs
index 67695dc2850..2aa20d02c88 100644
--- a/compiler/rustc_parse/src/validate_attr.rs
+++ b/compiler/rustc_parse/src/validate_attr.rs
@@ -4,7 +4,7 @@ use crate::parse_in;
 
 use rustc_ast::tokenstream::{DelimSpan, TokenTree};
 use rustc_ast::{self as ast, Attribute, MacArgs, MacDelimiter, MetaItem, MetaItemKind};
-use rustc_errors::{Applicability, PResult};
+use rustc_errors::{Applicability, FatalError, PResult};
 use rustc_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP};
 use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT;
 use rustc_session::parse::ParseSess;
@@ -91,69 +91,11 @@ pub fn check_builtin_attribute(
     // Some special attributes like `cfg` must be checked
     // before the generic check, so we skip them here.
     let should_skip = |name| name == sym::cfg;
-    // Some of previously accepted forms were used in practice,
-    // report them as warnings for now.
-    let should_warn = |name| {
-        name == sym::doc
-            || name == sym::ignore
-            || name == sym::inline
-            || name == sym::link
-            || name == sym::test
-            || name == sym::bench
-    };
 
     match parse_meta(sess, attr) {
         Ok(meta) => {
             if !should_skip(name) && !is_attr_template_compatible(&template, &meta.kind) {
-                let error_msg = format!("malformed `{}` attribute input", name);
-                let mut msg = "attribute must be of the form ".to_owned();
-                let mut suggestions = vec![];
-                let mut first = true;
-                if template.word {
-                    first = false;
-                    let code = format!("#[{}]", name);
-                    msg.push_str(&format!("`{}`", &code));
-                    suggestions.push(code);
-                }
-                if let Some(descr) = template.list {
-                    if !first {
-                        msg.push_str(" or ");
-                    }
-                    first = false;
-                    let code = format!("#[{}({})]", name, descr);
-                    msg.push_str(&format!("`{}`", &code));
-                    suggestions.push(code);
-                }
-                if let Some(descr) = template.name_value_str {
-                    if !first {
-                        msg.push_str(" or ");
-                    }
-                    let code = format!("#[{} = \"{}\"]", name, descr);
-                    msg.push_str(&format!("`{}`", &code));
-                    suggestions.push(code);
-                }
-                if should_warn(name) {
-                    sess.buffer_lint(
-                        &ILL_FORMED_ATTRIBUTE_INPUT,
-                        meta.span,
-                        ast::CRATE_NODE_ID,
-                        &msg,
-                    );
-                } else {
-                    sess.span_diagnostic
-                        .struct_span_err(meta.span, &error_msg)
-                        .span_suggestions(
-                            meta.span,
-                            if suggestions.len() == 1 {
-                                "must be of the form"
-                            } else {
-                                "the following are the possible correct uses"
-                            },
-                            suggestions.into_iter(),
-                            Applicability::HasPlaceholders,
-                        )
-                        .emit();
-                }
+                emit_malformed_attribute(sess, attr, name, template);
             }
         }
         Err(mut err) => {
@@ -161,3 +103,74 @@ pub fn check_builtin_attribute(
         }
     }
 }
+
+fn emit_malformed_attribute(
+    sess: &ParseSess,
+    attr: &Attribute,
+    name: Symbol,
+    template: AttributeTemplate,
+) {
+    // Some of previously accepted forms were used in practice,
+    // report them as warnings for now.
+    let should_warn = |name| {
+        matches!(name, sym::doc | sym::ignore | sym::inline | sym::link | sym::test | sym::bench)
+    };
+
+    let error_msg = format!("malformed `{}` attribute input", name);
+    let mut msg = "attribute must be of the form ".to_owned();
+    let mut suggestions = vec![];
+    let mut first = true;
+    let inner = if attr.style == ast::AttrStyle::Inner { "!" } else { "" };
+    if template.word {
+        first = false;
+        let code = format!("#{}[{}]", inner, name);
+        msg.push_str(&format!("`{}`", &code));
+        suggestions.push(code);
+    }
+    if let Some(descr) = template.list {
+        if !first {
+            msg.push_str(" or ");
+        }
+        first = false;
+        let code = format!("#{}[{}({})]", inner, name, descr);
+        msg.push_str(&format!("`{}`", &code));
+        suggestions.push(code);
+    }
+    if let Some(descr) = template.name_value_str {
+        if !first {
+            msg.push_str(" or ");
+        }
+        let code = format!("#{}[{} = \"{}\"]", inner, name, descr);
+        msg.push_str(&format!("`{}`", &code));
+        suggestions.push(code);
+    }
+    if should_warn(name) {
+        sess.buffer_lint(&ILL_FORMED_ATTRIBUTE_INPUT, attr.span, ast::CRATE_NODE_ID, &msg);
+    } else {
+        sess.span_diagnostic
+            .struct_span_err(attr.span, &error_msg)
+            .span_suggestions(
+                attr.span,
+                if suggestions.len() == 1 {
+                    "must be of the form"
+                } else {
+                    "the following are the possible correct uses"
+                },
+                suggestions.into_iter(),
+                Applicability::HasPlaceholders,
+            )
+            .emit();
+    }
+}
+
+pub fn emit_fatal_malformed_builtin_attribute(
+    sess: &ParseSess,
+    attr: &Attribute,
+    name: Symbol,
+) -> ! {
+    let template = BUILTIN_ATTRIBUTE_MAP.get(&name).expect("builtin attr defined").2;
+    emit_malformed_attribute(sess, attr, name, template);
+    // This is fatal, otherwise it will likely cause a cascade of other errors
+    // (and an error here is expected to be very rare).
+    FatalError.raise()
+}