]> git.lizzy.rs Git - rust.git/blob - src/librustc_parse/validate_attr.rs
Merge branch 'master' into relnotes-1.42.0
[rust.git] / src / librustc_parse / validate_attr.rs
1 //! Meta-syntax validation logic of attributes for post-expansion.
2
3 use crate::parse_in;
4
5 use rustc_errors::{Applicability, PResult};
6 use rustc_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP};
7 use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT;
8 use rustc_session::parse::ParseSess;
9 use rustc_span::{sym, Symbol};
10 use syntax::ast::{self, Attribute, MacArgs, MacDelimiter, MetaItem, MetaItemKind};
11 use syntax::tokenstream::DelimSpan;
12
13 pub fn check_meta(sess: &ParseSess, attr: &Attribute) {
14     if attr.is_doc_comment() {
15         return;
16     }
17
18     let attr_info =
19         attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)).map(|a| **a);
20
21     // Check input tokens for built-in and key-value attributes.
22     match attr_info {
23         // `rustc_dummy` doesn't have any restrictions specific to built-in attributes.
24         Some((name, _, template, _)) if name != sym::rustc_dummy => {
25             check_builtin_attribute(sess, attr, name, template)
26         }
27         _ => {
28             if let MacArgs::Eq(..) = attr.get_normal_item().args {
29                 // All key-value attributes are restricted to meta-item syntax.
30                 parse_meta(sess, attr)
31                     .map_err(|mut err| {
32                         err.emit();
33                     })
34                     .ok();
35             }
36         }
37     }
38 }
39
40 pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, MetaItem> {
41     let item = attr.get_normal_item();
42     Ok(MetaItem {
43         span: attr.span,
44         path: item.path.clone(),
45         kind: match &item.args {
46             MacArgs::Empty => MetaItemKind::Word,
47             MacArgs::Eq(_, t) => {
48                 let v = parse_in(sess, t.clone(), "name value", |p| p.parse_unsuffixed_lit())?;
49                 MetaItemKind::NameValue(v)
50             }
51             MacArgs::Delimited(dspan, delim, t) => {
52                 check_meta_bad_delim(sess, *dspan, *delim, "wrong meta list delimiters");
53                 let nmis = parse_in(sess, t.clone(), "meta list", |p| p.parse_meta_seq_top())?;
54                 MetaItemKind::List(nmis)
55             }
56         },
57     })
58 }
59
60 crate fn check_meta_bad_delim(sess: &ParseSess, span: DelimSpan, delim: MacDelimiter, msg: &str) {
61     if let ast::MacDelimiter::Parenthesis = delim {
62         return;
63     }
64
65     sess.span_diagnostic
66         .struct_span_err(span.entire(), msg)
67         .multipart_suggestion(
68             "the delimiters should be `(` and `)`",
69             vec![(span.open, "(".to_string()), (span.close, ")".to_string())],
70             Applicability::MachineApplicable,
71         )
72         .emit();
73 }
74
75 /// Checks that the given meta-item is compatible with this `AttributeTemplate`.
76 fn is_attr_template_compatible(template: &AttributeTemplate, meta: &ast::MetaItemKind) -> bool {
77     match meta {
78         MetaItemKind::Word => template.word,
79         MetaItemKind::List(..) => template.list.is_some(),
80         MetaItemKind::NameValue(lit) if lit.kind.is_str() => template.name_value_str.is_some(),
81         MetaItemKind::NameValue(..) => false,
82     }
83 }
84
85 pub fn check_builtin_attribute(
86     sess: &ParseSess,
87     attr: &Attribute,
88     name: Symbol,
89     template: AttributeTemplate,
90 ) {
91     // Some special attributes like `cfg` must be checked
92     // before the generic check, so we skip them here.
93     let should_skip = |name| name == sym::cfg;
94     // Some of previously accepted forms were used in practice,
95     // report them as warnings for now.
96     let should_warn = |name| {
97         name == sym::doc
98             || name == sym::ignore
99             || name == sym::inline
100             || name == sym::link
101             || name == sym::test
102             || name == sym::bench
103     };
104
105     match parse_meta(sess, attr) {
106         Ok(meta) => {
107             if !should_skip(name) && !is_attr_template_compatible(&template, &meta.kind) {
108                 let error_msg = format!("malformed `{}` attribute input", name);
109                 let mut msg = "attribute must be of the form ".to_owned();
110                 let mut suggestions = vec![];
111                 let mut first = true;
112                 if template.word {
113                     first = false;
114                     let code = format!("#[{}]", name);
115                     msg.push_str(&format!("`{}`", &code));
116                     suggestions.push(code);
117                 }
118                 if let Some(descr) = template.list {
119                     if !first {
120                         msg.push_str(" or ");
121                     }
122                     first = false;
123                     let code = format!("#[{}({})]", name, descr);
124                     msg.push_str(&format!("`{}`", &code));
125                     suggestions.push(code);
126                 }
127                 if let Some(descr) = template.name_value_str {
128                     if !first {
129                         msg.push_str(" or ");
130                     }
131                     let code = format!("#[{} = \"{}\"]", name, descr);
132                     msg.push_str(&format!("`{}`", &code));
133                     suggestions.push(code);
134                 }
135                 if should_warn(name) {
136                     sess.buffer_lint(
137                         &ILL_FORMED_ATTRIBUTE_INPUT,
138                         meta.span,
139                         ast::CRATE_NODE_ID,
140                         &msg,
141                     );
142                 } else {
143                     sess.span_diagnostic
144                         .struct_span_err(meta.span, &error_msg)
145                         .span_suggestions(
146                             meta.span,
147                             if suggestions.len() == 1 {
148                                 "must be of the form"
149                             } else {
150                                 "the following are the possible correct uses"
151                             },
152                             suggestions.into_iter(),
153                             Applicability::HasPlaceholders,
154                         )
155                         .emit();
156                 }
157             }
158         }
159         Err(mut err) => {
160             err.emit();
161         }
162     }
163 }