]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_parse/src/validate_attr.rs
Rollup merge of #104422 - compiler-errors:fix-suggest_associated_call_syntax, r=BoxyUwU
[rust.git] / compiler / rustc_parse / src / validate_attr.rs
1 //! Meta-syntax validation logic of attributes for post-expansion.
2
3 use crate::parse_in;
4
5 use rustc_ast::tokenstream::DelimSpan;
6 use rustc_ast::{self as ast, Attribute, MacArgs, MacArgsEq, MacDelimiter, MetaItem, MetaItemKind};
7 use rustc_ast_pretty::pprust;
8 use rustc_errors::{Applicability, FatalError, PResult};
9 use rustc_feature::{AttributeTemplate, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
10 use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT;
11 use rustc_session::parse::ParseSess;
12 use rustc_span::{sym, Symbol};
13
14 pub fn check_meta(sess: &ParseSess, attr: &Attribute) {
15     if attr.is_doc_comment() {
16         return;
17     }
18
19     let attr_info = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name));
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(BuiltinAttribute { name, template, .. }) if *name != sym::rustc_dummy => {
25             check_builtin_attribute(sess, attr, *name, *template)
26         }
27         _ if let MacArgs::Eq(..) = attr.get_normal_item().args => {
28             // All key-value attributes are restricted to meta-item syntax.
29             parse_meta(sess, attr)
30                 .map_err(|mut err| {
31                     err.emit();
32                 })
33                 .ok();
34         }
35         _ => {}
36     }
37 }
38
39 pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, MetaItem> {
40     let item = attr.get_normal_item();
41     Ok(MetaItem {
42         span: attr.span,
43         path: item.path.clone(),
44         kind: match &item.args {
45             MacArgs::Empty => MetaItemKind::Word,
46             MacArgs::Delimited(dspan, delim, t) => {
47                 check_meta_bad_delim(sess, *dspan, *delim, "wrong meta list delimiters");
48                 let nmis = parse_in(sess, t.clone(), "meta list", |p| p.parse_meta_seq_top())?;
49                 MetaItemKind::List(nmis)
50             }
51             MacArgs::Eq(_, MacArgsEq::Ast(expr)) => {
52                 if let ast::ExprKind::Lit(lit) = &expr.kind {
53                     if !lit.kind.is_unsuffixed() {
54                         let mut err = sess.span_diagnostic.struct_span_err(
55                             lit.span,
56                             "suffixed literals are not allowed in attributes",
57                         );
58                         err.help(
59                             "instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), \
60                             use an unsuffixed version (`1`, `1.0`, etc.)",
61                         );
62                         return Err(err);
63                     } else {
64                         MetaItemKind::NameValue(lit.clone())
65                     }
66                 } else {
67                     // The non-error case can happen with e.g. `#[foo = 1+1]`. The error case can
68                     // happen with e.g. `#[foo = include_str!("non-existent-file.rs")]`; in that
69                     // case we delay the error because an earlier error will have already been
70                     // reported.
71                     let msg = format!("unexpected expression: `{}`", pprust::expr_to_string(expr));
72                     let mut err = sess.span_diagnostic.struct_span_err(expr.span, msg);
73                     if let ast::ExprKind::Err = expr.kind {
74                         err.downgrade_to_delayed_bug();
75                     }
76                     return Err(err);
77                 }
78             }
79             MacArgs::Eq(_, MacArgsEq::Hir(lit)) => MetaItemKind::NameValue(lit.clone()),
80         },
81     })
82 }
83
84 pub fn check_meta_bad_delim(sess: &ParseSess, span: DelimSpan, delim: MacDelimiter, msg: &str) {
85     if let ast::MacDelimiter::Parenthesis = delim {
86         return;
87     }
88
89     sess.span_diagnostic
90         .struct_span_err(span.entire(), msg)
91         .multipart_suggestion(
92             "the delimiters should be `(` and `)`",
93             vec![(span.open, "(".to_string()), (span.close, ")".to_string())],
94             Applicability::MachineApplicable,
95         )
96         .emit();
97 }
98
99 /// Checks that the given meta-item is compatible with this `AttributeTemplate`.
100 fn is_attr_template_compatible(template: &AttributeTemplate, meta: &ast::MetaItemKind) -> bool {
101     match meta {
102         MetaItemKind::Word => template.word,
103         MetaItemKind::List(..) => template.list.is_some(),
104         MetaItemKind::NameValue(lit) if lit.kind.is_str() => template.name_value_str.is_some(),
105         MetaItemKind::NameValue(..) => false,
106     }
107 }
108
109 pub fn check_builtin_attribute(
110     sess: &ParseSess,
111     attr: &Attribute,
112     name: Symbol,
113     template: AttributeTemplate,
114 ) {
115     // Some special attributes like `cfg` must be checked
116     // before the generic check, so we skip them here.
117     let should_skip = |name| name == sym::cfg;
118
119     match parse_meta(sess, attr) {
120         Ok(meta) => {
121             if !should_skip(name) && !is_attr_template_compatible(&template, &meta.kind) {
122                 emit_malformed_attribute(sess, attr, name, template);
123             }
124         }
125         Err(mut err) => {
126             err.emit();
127         }
128     }
129 }
130
131 fn emit_malformed_attribute(
132     sess: &ParseSess,
133     attr: &Attribute,
134     name: Symbol,
135     template: AttributeTemplate,
136 ) {
137     // Some of previously accepted forms were used in practice,
138     // report them as warnings for now.
139     let should_warn = |name| {
140         matches!(name, sym::doc | sym::ignore | sym::inline | sym::link | sym::test | sym::bench)
141     };
142
143     let error_msg = format!("malformed `{}` attribute input", name);
144     let mut msg = "attribute must be of the form ".to_owned();
145     let mut suggestions = vec![];
146     let mut first = true;
147     let inner = if attr.style == ast::AttrStyle::Inner { "!" } else { "" };
148     if template.word {
149         first = false;
150         let code = format!("#{}[{}]", inner, name);
151         msg.push_str(&format!("`{}`", &code));
152         suggestions.push(code);
153     }
154     if let Some(descr) = template.list {
155         if !first {
156             msg.push_str(" or ");
157         }
158         first = false;
159         let code = format!("#{}[{}({})]", inner, name, descr);
160         msg.push_str(&format!("`{}`", &code));
161         suggestions.push(code);
162     }
163     if let Some(descr) = template.name_value_str {
164         if !first {
165             msg.push_str(" or ");
166         }
167         let code = format!("#{}[{} = \"{}\"]", inner, name, descr);
168         msg.push_str(&format!("`{}`", &code));
169         suggestions.push(code);
170     }
171     if should_warn(name) {
172         sess.buffer_lint(&ILL_FORMED_ATTRIBUTE_INPUT, attr.span, ast::CRATE_NODE_ID, &msg);
173     } else {
174         sess.span_diagnostic
175             .struct_span_err(attr.span, &error_msg)
176             .span_suggestions(
177                 attr.span,
178                 if suggestions.len() == 1 {
179                     "must be of the form"
180                 } else {
181                     "the following are the possible correct uses"
182                 },
183                 suggestions.into_iter(),
184                 Applicability::HasPlaceholders,
185             )
186             .emit();
187     }
188 }
189
190 pub fn emit_fatal_malformed_builtin_attribute(
191     sess: &ParseSess,
192     attr: &Attribute,
193     name: Symbol,
194 ) -> ! {
195     let template = BUILTIN_ATTRIBUTE_MAP.get(&name).expect("builtin attr defined").template;
196     emit_malformed_attribute(sess, attr, name, template);
197     // This is fatal, otherwise it will likely cause a cascade of other errors
198     // (and an error here is expected to be very rare).
199     FatalError.raise()
200 }