]> git.proxmox.com Git - rustc.git/blobdiff - vendor/rustc-ap-rustc_parse/src/validate_attr.rs
Update upstream source from tag 'upstream/1.52.1+dfsg1'
[rustc.git] / vendor / rustc-ap-rustc_parse / src / validate_attr.rs
diff --git a/vendor/rustc-ap-rustc_parse/src/validate_attr.rs b/vendor/rustc-ap-rustc_parse/src/validate_attr.rs
new file mode 100644 (file)
index 0000000..2137272
--- /dev/null
@@ -0,0 +1,164 @@
+//! Meta-syntax validation logic of attributes for post-expansion.
+
+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_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP};
+use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT;
+use rustc_session::parse::ParseSess;
+use rustc_span::{sym, Symbol};
+
+pub fn check_meta(sess: &ParseSess, attr: &Attribute) {
+    if attr.is_doc_comment() {
+        return;
+    }
+
+    let attr_info =
+        attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)).map(|a| **a);
+
+    // Check input tokens for built-in and key-value attributes.
+    match attr_info {
+        // `rustc_dummy` doesn't have any restrictions specific to built-in attributes.
+        Some((name, _, template, _)) if name != sym::rustc_dummy => {
+            check_builtin_attribute(sess, attr, name, template)
+        }
+        _ => {
+            if let MacArgs::Eq(..) = attr.get_normal_item().args {
+                // All key-value attributes are restricted to meta-item syntax.
+                parse_meta(sess, attr)
+                    .map_err(|mut err| {
+                        err.emit();
+                    })
+                    .ok();
+            }
+        }
+    }
+}
+
+pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, MetaItem> {
+    let item = attr.get_normal_item();
+    Ok(MetaItem {
+        span: attr.span,
+        path: item.path.clone(),
+        kind: match &item.args {
+            MacArgs::Empty => MetaItemKind::Word,
+            MacArgs::Eq(_, t) => {
+                let t = TokenTree::Token(t.clone()).into();
+                let v = parse_in(sess, t, "name value", |p| p.parse_unsuffixed_lit())?;
+                MetaItemKind::NameValue(v)
+            }
+            MacArgs::Delimited(dspan, delim, t) => {
+                check_meta_bad_delim(sess, *dspan, *delim, "wrong meta list delimiters");
+                let nmis = parse_in(sess, t.clone(), "meta list", |p| p.parse_meta_seq_top())?;
+                MetaItemKind::List(nmis)
+            }
+        },
+    })
+}
+
+pub fn check_meta_bad_delim(sess: &ParseSess, span: DelimSpan, delim: MacDelimiter, msg: &str) {
+    if let ast::MacDelimiter::Parenthesis = delim {
+        return;
+    }
+
+    sess.span_diagnostic
+        .struct_span_err(span.entire(), msg)
+        .multipart_suggestion(
+            "the delimiters should be `(` and `)`",
+            vec![(span.open, "(".to_string()), (span.close, ")".to_string())],
+            Applicability::MachineApplicable,
+        )
+        .emit();
+}
+
+/// Checks that the given meta-item is compatible with this `AttributeTemplate`.
+fn is_attr_template_compatible(template: &AttributeTemplate, meta: &ast::MetaItemKind) -> bool {
+    match meta {
+        MetaItemKind::Word => template.word,
+        MetaItemKind::List(..) => template.list.is_some(),
+        MetaItemKind::NameValue(lit) if lit.kind.is_str() => template.name_value_str.is_some(),
+        MetaItemKind::NameValue(..) => false,
+    }
+}
+
+pub fn check_builtin_attribute(
+    sess: &ParseSess,
+    attr: &Attribute,
+    name: Symbol,
+    template: AttributeTemplate,
+) {
+    // 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();
+                }
+            }
+        }
+        Err(mut err) => {
+            err.emit();
+        }
+    }
+}