1use std::convert::identity;
4use std::slice;
5
6use rustc_ast::token::Delimiter;
7use rustc_ast::tokenstream::DelimSpan;
8use rustc_ast::{
9 self as ast, AttrArgs, Attribute, DelimArgs, MetaItem, MetaItemInner, MetaItemKind, Safety,
10};
11use rustc_errors::{Applicability, FatalError, PResult};
12use rustc_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute};
13use rustc_hir::AttrPath;
14use rustc_hir::lints::AttributeLintKind;
15use rustc_parse::parse_in;
16use rustc_session::errors::report_lit_error;
17use rustc_session::lint::BuiltinLintDiag;
18use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT;
19use rustc_session::parse::ParseSess;
20use rustc_span::{Span, Symbol, sym};
21
22use crate::{AttributeParser, Late, session_diagnostics as errors};
23
24pub fn check_attr(psess: &ParseSess, attr: &Attribute) {
25 if attr.is_doc_comment() || attr.has_name(sym::cfg_trace) || attr.has_name(sym::cfg_attr_trace)
26 {
27 return;
28 }
29
30 let builtin_attr_info = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name));
31
32 match builtin_attr_info {
34 Some(BuiltinAttribute { name, template, .. }) => {
36 if AttributeParser::<Late>::is_parsed_attribute(slice::from_ref(&name)) {
37 return;
38 }
39 match parse_meta(psess, attr) {
40 Ok(meta) => {
42 check_builtin_meta_item(psess, &meta, attr.style, *name, *template, false)
43 }
44 Err(err) => {
45 err.emit();
46 }
47 }
48 }
49 _ => {
50 let attr_item = attr.get_normal_item();
51 if let AttrArgs::Eq { .. } = attr_item.args {
52 match parse_meta(psess, attr) {
54 Ok(_) => {}
55 Err(err) => {
56 err.emit();
57 }
58 }
59 }
60 }
61 }
62}
63
64pub fn parse_meta<'a>(psess: &'a ParseSess, attr: &Attribute) -> PResult<'a, MetaItem> {
65 let item = attr.get_normal_item();
66 Ok(MetaItem {
67 unsafety: item.unsafety,
68 span: attr.span,
69 path: item.path.clone(),
70 kind: match &item.args {
71 AttrArgs::Empty => MetaItemKind::Word,
72 AttrArgs::Delimited(DelimArgs { dspan, delim, tokens }) => {
73 check_meta_bad_delim(psess, *dspan, *delim);
74 let nmis =
75 parse_in(psess, tokens.clone(), "meta list", |p| p.parse_meta_seq_top())?;
76 MetaItemKind::List(nmis)
77 }
78 AttrArgs::Eq { expr, .. } => {
79 if let ast::ExprKind::Lit(token_lit) = expr.kind {
80 let res = ast::MetaItemLit::from_token_lit(token_lit, expr.span);
81 let res = match res {
82 Ok(lit) => {
83 if token_lit.suffix.is_some() {
84 let mut err = psess.dcx().struct_span_err(
85 expr.span,
86 "suffixed literals are not allowed in attributes",
87 );
88 err.help(
89 "instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), \
90 use an unsuffixed version (`1`, `1.0`, etc.)",
91 );
92 return Err(err);
93 } else {
94 MetaItemKind::NameValue(lit)
95 }
96 }
97 Err(err) => {
98 let guar = report_lit_error(psess, err, token_lit, expr.span);
99 let lit = ast::MetaItemLit {
100 symbol: token_lit.symbol,
101 suffix: token_lit.suffix,
102 kind: ast::LitKind::Err(guar),
103 span: expr.span,
104 };
105 MetaItemKind::NameValue(lit)
106 }
107 };
108 res
109 } else {
110 let msg = "attribute value must be a literal";
117 let mut err = psess.dcx().struct_span_err(expr.span, msg);
118 if let ast::ExprKind::Err(_) = expr.kind {
119 err.downgrade_to_delayed_bug();
120 }
121 return Err(err);
122 }
123 }
124 },
125 })
126}
127
128fn check_meta_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter) {
129 if let Delimiter::Parenthesis = delim {
130 return;
131 }
132 psess.dcx().emit_err(errors::MetaBadDelim {
133 span: span.entire(),
134 sugg: errors::MetaBadDelimSugg { open: span.open, close: span.close },
135 });
136}
137
138fn is_attr_template_compatible(template: &AttributeTemplate, meta: &ast::MetaItemKind) -> bool {
140 let is_one_allowed_subword = |items: &[MetaItemInner]| match items {
141 [item] => item.is_word() && template.one_of.iter().any(|&word| item.has_name(word)),
142 _ => false,
143 };
144 match meta {
145 MetaItemKind::Word => template.word,
146 MetaItemKind::List(items) => template.list.is_some() || is_one_allowed_subword(items),
147 MetaItemKind::NameValue(lit) if lit.kind.is_str() => template.name_value_str.is_some(),
148 MetaItemKind::NameValue(..) => false,
149 }
150}
151
152pub fn check_builtin_meta_item(
153 psess: &ParseSess,
154 meta: &MetaItem,
155 style: ast::AttrStyle,
156 name: Symbol,
157 template: AttributeTemplate,
158 deny_unsafety: bool,
159) {
160 if !is_attr_template_compatible(&template, &meta.kind) {
161 emit_malformed_attribute(psess, style, meta.span, name, template);
163 }
164
165 if deny_unsafety && let Safety::Unsafe(unsafe_span) = meta.unsafety {
166 psess.dcx().emit_err(errors::InvalidAttrUnsafe {
167 span: unsafe_span,
168 name: AttrPath::from_ast(&meta.path, identity),
169 });
170 }
171}
172
173fn emit_malformed_attribute(
174 psess: &ParseSess,
175 style: ast::AttrStyle,
176 span: Span,
177 name: Symbol,
178 template: AttributeTemplate,
179) {
180 let should_warn = |name| matches!(name, sym::doc | sym::link | sym::test | sym::bench);
183
184 let error_msg = format!("malformed `{name}` attribute input");
185 let mut suggestions = vec![];
186 let inner = if style == ast::AttrStyle::Inner { "!" } else { "" };
187 if template.word {
188 suggestions.push(format!("#{inner}[{name}]"));
189 }
190 if let Some(descr) = template.list {
191 for descr in descr {
192 suggestions.push(format!("#{inner}[{name}({descr})]"));
193 }
194 }
195 suggestions.extend(template.one_of.iter().map(|&word| format!("#{inner}[{name}({word})]")));
196 if let Some(descr) = template.name_value_str {
197 for descr in descr {
198 suggestions.push(format!("#{inner}[{name} = \"{descr}\"]"));
199 }
200 }
201 if suggestions.len() > 3 {
204 suggestions.clear();
205 }
206 if should_warn(name) {
207 psess.buffer_lint(
208 ILL_FORMED_ATTRIBUTE_INPUT,
209 span,
210 ast::CRATE_NODE_ID,
211 BuiltinLintDiag::AttributeLint(AttributeLintKind::IllFormedAttributeInput {
212 suggestions: suggestions.clone(),
213 docs: template.docs,
214 }),
215 );
216 } else {
217 suggestions.sort();
218 let mut err = psess.dcx().struct_span_err(span, error_msg).with_span_suggestions(
219 span,
220 if suggestions.len() == 1 {
221 "must be of the form"
222 } else {
223 "the following are the possible correct uses"
224 },
225 suggestions,
226 Applicability::HasPlaceholders,
227 );
228 if let Some(link) = template.docs {
229 err.note(format!("for more information, visit <{link}>"));
230 }
231 err.emit();
232 }
233}
234
235pub fn emit_fatal_malformed_builtin_attribute(
236 psess: &ParseSess,
237 attr: &Attribute,
238 name: Symbol,
239) -> ! {
240 let template = BUILTIN_ATTRIBUTE_MAP.get(&name).expect("builtin attr defined").template;
241 emit_malformed_attribute(psess, attr.style, attr.span, name, template);
242 FatalError.raise()
245}