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