]>
Commit | Line | Data |
---|---|---|
8faf50e0 XL |
1 | //! Parsing and validation of builtin attributes |
2 | ||
532ac7d7 | 3 | use crate::ast::{self, Attribute, MetaItem, NestedMetaItem}; |
416331ca | 4 | use crate::early_buffered_lints::BufferedEarlyLintId; |
9fa01778 | 5 | use crate::feature_gate::{Features, GatedCfg}; |
e74abb32 XL |
6 | use crate::print::pprust; |
7 | use crate::sess::ParseSess; | |
9fa01778 | 8 | |
8faf50e0 | 9 | use errors::{Applicability, Handler}; |
416331ca | 10 | use syntax_pos::hygiene::Transparency; |
48663c56 | 11 | use syntax_pos::{symbol::Symbol, symbol::sym, Span}; |
8faf50e0 | 12 | |
532ac7d7 | 13 | use super::{mark_used, MetaItemKind}; |
8faf50e0 XL |
14 | |
15 | enum AttrError { | |
9fa01778 XL |
16 | MultipleItem(String), |
17 | UnknownMetaItem(String, &'static [&'static str]), | |
8faf50e0 XL |
18 | MissingSince, |
19 | MissingFeature, | |
20 | MultipleStabilityLevels, | |
a1dfa0c6 | 21 | UnsupportedLiteral(&'static str, /* is_bytestr */ bool), |
8faf50e0 XL |
22 | } |
23 | ||
416331ca XL |
24 | /// A template that the attribute input must match. |
25 | /// Only top-level shape (`#[attr]` vs `#[attr(...)]` vs `#[attr = ...]`) is considered now. | |
26 | #[derive(Clone, Copy)] | |
27 | pub struct AttributeTemplate { | |
28 | crate word: bool, | |
29 | crate list: Option<&'static str>, | |
30 | crate name_value_str: Option<&'static str>, | |
31 | } | |
32 | ||
33 | impl AttributeTemplate { | |
e74abb32 XL |
34 | pub fn only_word() -> Self { |
35 | Self { word: true, list: None, name_value_str: None } | |
36 | } | |
37 | ||
416331ca XL |
38 | /// Checks that the given meta-item is compatible with this template. |
39 | fn compatible(&self, meta_item_kind: &ast::MetaItemKind) -> bool { | |
40 | match meta_item_kind { | |
41 | ast::MetaItemKind::Word => self.word, | |
42 | ast::MetaItemKind::List(..) => self.list.is_some(), | |
e74abb32 | 43 | ast::MetaItemKind::NameValue(lit) if lit.kind.is_str() => self.name_value_str.is_some(), |
416331ca XL |
44 | ast::MetaItemKind::NameValue(..) => false, |
45 | } | |
46 | } | |
47 | } | |
48 | ||
a1dfa0c6 XL |
49 | fn handle_errors(sess: &ParseSess, span: Span, error: AttrError) { |
50 | let diag = &sess.span_diagnostic; | |
8faf50e0 XL |
51 | match error { |
52 | AttrError::MultipleItem(item) => span_err!(diag, span, E0538, | |
53 | "multiple '{}' items", item), | |
54 | AttrError::UnknownMetaItem(item, expected) => { | |
55 | let expected = expected | |
56 | .iter() | |
57 | .map(|name| format!("`{}`", name)) | |
58 | .collect::<Vec<_>>(); | |
59 | struct_span_err!(diag, span, E0541, "unknown meta item '{}'", item) | |
60 | .span_label(span, format!("expected one of {}", expected.join(", "))) | |
61 | .emit(); | |
62 | } | |
63 | AttrError::MissingSince => span_err!(diag, span, E0542, "missing 'since'"), | |
64 | AttrError::MissingFeature => span_err!(diag, span, E0546, "missing 'feature'"), | |
65 | AttrError::MultipleStabilityLevels => span_err!(diag, span, E0544, | |
66 | "multiple stability levels"), | |
a1dfa0c6 XL |
67 | AttrError::UnsupportedLiteral( |
68 | msg, | |
69 | is_bytestr, | |
70 | ) => { | |
71 | let mut err = struct_span_err!(diag, span, E0565, "{}", msg); | |
72 | if is_bytestr { | |
73 | if let Ok(lint_str) = sess.source_map().span_to_snippet(span) { | |
9fa01778 | 74 | err.span_suggestion( |
a1dfa0c6 XL |
75 | span, |
76 | "consider removing the prefix", | |
77 | format!("{}", &lint_str[1..]), | |
78 | Applicability::MaybeIncorrect, | |
79 | ); | |
80 | } | |
81 | } | |
82 | err.emit(); | |
83 | } | |
8faf50e0 XL |
84 | } |
85 | } | |
86 | ||
e74abb32 | 87 | #[derive(Clone, PartialEq, RustcEncodable, RustcDecodable)] |
8faf50e0 XL |
88 | pub enum InlineAttr { |
89 | None, | |
90 | Hint, | |
91 | Always, | |
92 | Never, | |
93 | } | |
94 | ||
e74abb32 | 95 | #[derive(Clone, RustcEncodable, RustcDecodable)] |
9fa01778 XL |
96 | pub enum OptimizeAttr { |
97 | None, | |
98 | Speed, | |
99 | Size, | |
100 | } | |
101 | ||
8faf50e0 XL |
102 | #[derive(Copy, Clone, PartialEq)] |
103 | pub enum UnwindAttr { | |
104 | Allowed, | |
105 | Aborts, | |
106 | } | |
107 | ||
108 | /// Determine what `#[unwind]` attribute is present in `attrs`, if any. | |
109 | pub fn find_unwind_attr(diagnostic: Option<&Handler>, attrs: &[Attribute]) -> Option<UnwindAttr> { | |
8faf50e0 | 110 | attrs.iter().fold(None, |ia, attr| { |
48663c56 | 111 | if attr.check_name(sym::unwind) { |
532ac7d7 | 112 | if let Some(meta) = attr.meta() { |
e74abb32 | 113 | if let MetaItemKind::List(items) = meta.kind { |
532ac7d7 | 114 | if items.len() == 1 { |
48663c56 | 115 | if items[0].check_name(sym::allowed) { |
532ac7d7 | 116 | return Some(UnwindAttr::Allowed); |
48663c56 | 117 | } else if items[0].check_name(sym::aborts) { |
532ac7d7 XL |
118 | return Some(UnwindAttr::Aborts); |
119 | } | |
120 | } | |
121 | ||
122 | diagnostic.map(|d| { | |
dc9dc135 XL |
123 | struct_span_err!(d, attr.span, E0633, "malformed `unwind` attribute input") |
124 | .span_label(attr.span, "invalid argument") | |
125 | .span_suggestions( | |
126 | attr.span, | |
127 | "the allowed arguments are `allowed` and `aborts`", | |
128 | (vec!["allowed", "aborts"]).into_iter() | |
129 | .map(|s| format!("#[unwind({})]", s)), | |
130 | Applicability::MachineApplicable, | |
131 | ).emit(); | |
532ac7d7 | 132 | }); |
8faf50e0 XL |
133 | } |
134 | } | |
8faf50e0 | 135 | } |
532ac7d7 XL |
136 | |
137 | ia | |
8faf50e0 XL |
138 | }) |
139 | } | |
140 | ||
141 | /// Represents the #[stable], #[unstable], #[rustc_{deprecated,const_unstable}] attributes. | |
dc9dc135 | 142 | #[derive(RustcEncodable, RustcDecodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] |
8faf50e0 XL |
143 | pub struct Stability { |
144 | pub level: StabilityLevel, | |
145 | pub feature: Symbol, | |
146 | pub rustc_depr: Option<RustcDeprecation>, | |
0bf4aa26 | 147 | /// `None` means the function is stable but needs to be a stable const fn, too |
b7449926 XL |
148 | /// `Some` contains the feature gate required to be able to use the function |
149 | /// as const fn | |
150 | pub const_stability: Option<Symbol>, | |
0bf4aa26 XL |
151 | /// whether the function has a `#[rustc_promotable]` attribute |
152 | pub promotable: bool, | |
48663c56 XL |
153 | /// whether the function has a `#[rustc_allow_const_fn_ptr]` attribute |
154 | pub allow_const_fn_ptr: bool, | |
8faf50e0 XL |
155 | } |
156 | ||
157 | /// The available stability levels. | |
dc9dc135 | 158 | #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Copy, Clone, Debug, Eq, Hash)] |
8faf50e0 XL |
159 | pub enum StabilityLevel { |
160 | // Reason for the current stability level and the relevant rust-lang issue | |
416331ca | 161 | Unstable { reason: Option<Symbol>, issue: u32, is_soft: bool }, |
8faf50e0 XL |
162 | Stable { since: Symbol }, |
163 | } | |
164 | ||
165 | impl StabilityLevel { | |
166 | pub fn is_unstable(&self) -> bool { | |
167 | if let StabilityLevel::Unstable {..} = *self { | |
168 | true | |
169 | } else { | |
170 | false | |
171 | } | |
172 | } | |
173 | pub fn is_stable(&self) -> bool { | |
174 | if let StabilityLevel::Stable {..} = *self { | |
175 | true | |
176 | } else { | |
177 | false | |
178 | } | |
179 | } | |
180 | } | |
181 | ||
dc9dc135 | 182 | #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Copy, Clone, Debug, Eq, Hash)] |
8faf50e0 XL |
183 | pub struct RustcDeprecation { |
184 | pub since: Symbol, | |
185 | pub reason: Symbol, | |
9fa01778 XL |
186 | /// A text snippet used to completely replace any use of the deprecated item in an expression. |
187 | pub suggestion: Option<Symbol>, | |
8faf50e0 XL |
188 | } |
189 | ||
9fa01778 | 190 | /// Checks if `attrs` contains an attribute like `#![feature(feature_name)]`. |
8faf50e0 | 191 | /// This will not perform any "sanity checks" on the form of the attributes. |
48663c56 | 192 | pub fn contains_feature_attr(attrs: &[Attribute], feature_name: Symbol) -> bool { |
8faf50e0 | 193 | attrs.iter().any(|item| { |
48663c56 | 194 | item.check_name(sym::feature) && |
8faf50e0 | 195 | item.meta_item_list().map(|list| { |
9fa01778 | 196 | list.iter().any(|mi| mi.is_word() && mi.check_name(feature_name)) |
8faf50e0 XL |
197 | }).unwrap_or(false) |
198 | }) | |
199 | } | |
200 | ||
416331ca XL |
201 | /// Collects stability info from all stability attributes in `attrs`. |
202 | /// Returns `None` if no stability attributes are found. | |
a1dfa0c6 | 203 | pub fn find_stability(sess: &ParseSess, attrs: &[Attribute], |
8faf50e0 | 204 | item_sp: Span) -> Option<Stability> { |
a1dfa0c6 | 205 | find_stability_generic(sess, attrs.iter(), item_sp) |
8faf50e0 XL |
206 | } |
207 | ||
a1dfa0c6 | 208 | fn find_stability_generic<'a, I>(sess: &ParseSess, |
8faf50e0 XL |
209 | attrs_iter: I, |
210 | item_sp: Span) | |
211 | -> Option<Stability> | |
212 | where I: Iterator<Item = &'a Attribute> | |
213 | { | |
9fa01778 | 214 | use StabilityLevel::*; |
8faf50e0 XL |
215 | |
216 | let mut stab: Option<Stability> = None; | |
217 | let mut rustc_depr: Option<RustcDeprecation> = None; | |
b7449926 | 218 | let mut rustc_const_unstable: Option<Symbol> = None; |
0bf4aa26 | 219 | let mut promotable = false; |
48663c56 | 220 | let mut allow_const_fn_ptr = false; |
a1dfa0c6 | 221 | let diagnostic = &sess.span_diagnostic; |
8faf50e0 XL |
222 | |
223 | 'outer: for attr in attrs_iter { | |
224 | if ![ | |
48663c56 XL |
225 | sym::rustc_deprecated, |
226 | sym::rustc_const_unstable, | |
227 | sym::unstable, | |
228 | sym::stable, | |
229 | sym::rustc_promotable, | |
230 | sym::rustc_allow_const_fn_ptr, | |
8faf50e0 XL |
231 | ].iter().any(|&s| attr.path == s) { |
232 | continue // not a stability level | |
233 | } | |
234 | ||
235 | mark_used(attr); | |
236 | ||
237 | let meta = attr.meta(); | |
0bf4aa26 | 238 | |
48663c56 | 239 | if attr.path == sym::rustc_promotable { |
0bf4aa26 XL |
240 | promotable = true; |
241 | } | |
48663c56 XL |
242 | if attr.path == sym::rustc_allow_const_fn_ptr { |
243 | allow_const_fn_ptr = true; | |
244 | } | |
b7449926 | 245 | // attributes with data |
e74abb32 | 246 | else if let Some(MetaItem { kind: MetaItemKind::List(ref metas), .. }) = meta { |
8faf50e0 XL |
247 | let meta = meta.as_ref().unwrap(); |
248 | let get = |meta: &MetaItem, item: &mut Option<Symbol>| { | |
249 | if item.is_some() { | |
e74abb32 XL |
250 | handle_errors( |
251 | sess, | |
252 | meta.span, | |
253 | AttrError::MultipleItem(pprust::path_to_string(&meta.path)), | |
254 | ); | |
8faf50e0 XL |
255 | return false |
256 | } | |
257 | if let Some(v) = meta.value_str() { | |
258 | *item = Some(v); | |
259 | true | |
260 | } else { | |
261 | span_err!(diagnostic, meta.span, E0539, "incorrect meta item"); | |
262 | false | |
263 | } | |
264 | }; | |
265 | ||
266 | macro_rules! get_meta { | |
267 | ($($name:ident),+) => { | |
268 | $( | |
269 | let mut $name = None; | |
270 | )+ | |
271 | for meta in metas { | |
272 | if let Some(mi) = meta.meta_item() { | |
48663c56 | 273 | match mi.name_or_empty() { |
8faf50e0 | 274 | $( |
48663c56 | 275 | sym::$name => if !get(mi, &mut $name) { continue 'outer }, |
8faf50e0 XL |
276 | )+ |
277 | _ => { | |
278 | let expected = &[ $( stringify!($name) ),+ ]; | |
279 | handle_errors( | |
a1dfa0c6 | 280 | sess, |
8faf50e0 | 281 | mi.span, |
e74abb32 XL |
282 | AttrError::UnknownMetaItem( |
283 | pprust::path_to_string(&mi.path), | |
284 | expected, | |
285 | ), | |
a1dfa0c6 | 286 | ); |
8faf50e0 XL |
287 | continue 'outer |
288 | } | |
289 | } | |
290 | } else { | |
a1dfa0c6 XL |
291 | handle_errors( |
292 | sess, | |
532ac7d7 | 293 | meta.span(), |
a1dfa0c6 XL |
294 | AttrError::UnsupportedLiteral( |
295 | "unsupported literal", | |
296 | false, | |
297 | ), | |
298 | ); | |
8faf50e0 XL |
299 | continue 'outer |
300 | } | |
301 | } | |
302 | } | |
303 | } | |
304 | ||
48663c56 XL |
305 | match meta.name_or_empty() { |
306 | sym::rustc_deprecated => { | |
8faf50e0 XL |
307 | if rustc_depr.is_some() { |
308 | span_err!(diagnostic, item_sp, E0540, | |
309 | "multiple rustc_deprecated attributes"); | |
310 | continue 'outer | |
311 | } | |
312 | ||
9fa01778 | 313 | get_meta!(since, reason, suggestion); |
8faf50e0 XL |
314 | |
315 | match (since, reason) { | |
316 | (Some(since), Some(reason)) => { | |
317 | rustc_depr = Some(RustcDeprecation { | |
318 | since, | |
319 | reason, | |
9fa01778 | 320 | suggestion, |
8faf50e0 XL |
321 | }) |
322 | } | |
323 | (None, _) => { | |
532ac7d7 | 324 | handle_errors(sess, attr.span, AttrError::MissingSince); |
8faf50e0 XL |
325 | continue |
326 | } | |
327 | _ => { | |
532ac7d7 | 328 | span_err!(diagnostic, attr.span, E0543, "missing 'reason'"); |
8faf50e0 XL |
329 | continue |
330 | } | |
331 | } | |
332 | } | |
48663c56 | 333 | sym::rustc_const_unstable => { |
8faf50e0 XL |
334 | if rustc_const_unstable.is_some() { |
335 | span_err!(diagnostic, item_sp, E0553, | |
336 | "multiple rustc_const_unstable attributes"); | |
337 | continue 'outer | |
338 | } | |
339 | ||
340 | get_meta!(feature); | |
341 | if let Some(feature) = feature { | |
b7449926 | 342 | rustc_const_unstable = Some(feature); |
8faf50e0 | 343 | } else { |
532ac7d7 | 344 | span_err!(diagnostic, attr.span, E0629, "missing 'feature'"); |
8faf50e0 XL |
345 | continue |
346 | } | |
347 | } | |
48663c56 | 348 | sym::unstable => { |
8faf50e0 | 349 | if stab.is_some() { |
532ac7d7 | 350 | handle_errors(sess, attr.span, AttrError::MultipleStabilityLevels); |
8faf50e0 XL |
351 | break |
352 | } | |
353 | ||
354 | let mut feature = None; | |
355 | let mut reason = None; | |
356 | let mut issue = None; | |
416331ca | 357 | let mut is_soft = false; |
8faf50e0 XL |
358 | for meta in metas { |
359 | if let Some(mi) = meta.meta_item() { | |
48663c56 XL |
360 | match mi.name_or_empty() { |
361 | sym::feature => if !get(mi, &mut feature) { continue 'outer }, | |
362 | sym::reason => if !get(mi, &mut reason) { continue 'outer }, | |
363 | sym::issue => if !get(mi, &mut issue) { continue 'outer }, | |
416331ca XL |
364 | sym::soft => { |
365 | if !mi.is_word() { | |
366 | let msg = "`soft` should not have any arguments"; | |
367 | sess.span_diagnostic.span_err(mi.span, msg); | |
368 | } | |
369 | is_soft = true; | |
370 | } | |
8faf50e0 XL |
371 | _ => { |
372 | handle_errors( | |
a1dfa0c6 | 373 | sess, |
532ac7d7 | 374 | meta.span(), |
8faf50e0 | 375 | AttrError::UnknownMetaItem( |
e74abb32 | 376 | pprust::path_to_string(&mi.path), |
416331ca | 377 | &["feature", "reason", "issue", "soft"] |
8faf50e0 XL |
378 | ), |
379 | ); | |
380 | continue 'outer | |
381 | } | |
382 | } | |
383 | } else { | |
a1dfa0c6 XL |
384 | handle_errors( |
385 | sess, | |
532ac7d7 | 386 | meta.span(), |
a1dfa0c6 XL |
387 | AttrError::UnsupportedLiteral( |
388 | "unsupported literal", | |
389 | false, | |
390 | ), | |
391 | ); | |
8faf50e0 XL |
392 | continue 'outer |
393 | } | |
394 | } | |
395 | ||
396 | match (feature, reason, issue) { | |
397 | (Some(feature), reason, Some(issue)) => { | |
398 | stab = Some(Stability { | |
399 | level: Unstable { | |
400 | reason, | |
401 | issue: { | |
402 | if let Ok(issue) = issue.as_str().parse() { | |
403 | issue | |
404 | } else { | |
532ac7d7 | 405 | span_err!(diagnostic, attr.span, E0545, |
8faf50e0 XL |
406 | "incorrect 'issue'"); |
407 | continue | |
408 | } | |
416331ca XL |
409 | }, |
410 | is_soft, | |
8faf50e0 XL |
411 | }, |
412 | feature, | |
413 | rustc_depr: None, | |
b7449926 | 414 | const_stability: None, |
0bf4aa26 | 415 | promotable: false, |
48663c56 | 416 | allow_const_fn_ptr: false, |
8faf50e0 XL |
417 | }) |
418 | } | |
419 | (None, _, _) => { | |
532ac7d7 | 420 | handle_errors(sess, attr.span, AttrError::MissingFeature); |
8faf50e0 XL |
421 | continue |
422 | } | |
423 | _ => { | |
532ac7d7 | 424 | span_err!(diagnostic, attr.span, E0547, "missing 'issue'"); |
8faf50e0 XL |
425 | continue |
426 | } | |
427 | } | |
428 | } | |
48663c56 | 429 | sym::stable => { |
8faf50e0 | 430 | if stab.is_some() { |
532ac7d7 | 431 | handle_errors(sess, attr.span, AttrError::MultipleStabilityLevels); |
8faf50e0 XL |
432 | break |
433 | } | |
434 | ||
435 | let mut feature = None; | |
436 | let mut since = None; | |
437 | for meta in metas { | |
532ac7d7 XL |
438 | match meta { |
439 | NestedMetaItem::MetaItem(mi) => { | |
48663c56 XL |
440 | match mi.name_or_empty() { |
441 | sym::feature => if !get(mi, &mut feature) { continue 'outer }, | |
442 | sym::since => if !get(mi, &mut since) { continue 'outer }, | |
a1dfa0c6 XL |
443 | _ => { |
444 | handle_errors( | |
445 | sess, | |
532ac7d7 | 446 | meta.span(), |
a1dfa0c6 | 447 | AttrError::UnknownMetaItem( |
e74abb32 XL |
448 | pprust::path_to_string(&mi.path), |
449 | &["since", "note"], | |
a1dfa0c6 XL |
450 | ), |
451 | ); | |
452 | continue 'outer | |
453 | } | |
8faf50e0 | 454 | } |
a1dfa0c6 | 455 | }, |
532ac7d7 | 456 | NestedMetaItem::Literal(lit) => { |
a1dfa0c6 XL |
457 | handle_errors( |
458 | sess, | |
459 | lit.span, | |
460 | AttrError::UnsupportedLiteral( | |
461 | "unsupported literal", | |
462 | false, | |
463 | ), | |
464 | ); | |
465 | continue 'outer | |
8faf50e0 | 466 | } |
8faf50e0 XL |
467 | } |
468 | } | |
469 | ||
470 | match (feature, since) { | |
471 | (Some(feature), Some(since)) => { | |
472 | stab = Some(Stability { | |
473 | level: Stable { | |
474 | since, | |
475 | }, | |
476 | feature, | |
477 | rustc_depr: None, | |
b7449926 | 478 | const_stability: None, |
0bf4aa26 | 479 | promotable: false, |
48663c56 | 480 | allow_const_fn_ptr: false, |
8faf50e0 XL |
481 | }) |
482 | } | |
483 | (None, _) => { | |
532ac7d7 | 484 | handle_errors(sess, attr.span, AttrError::MissingFeature); |
8faf50e0 XL |
485 | continue |
486 | } | |
487 | _ => { | |
532ac7d7 | 488 | handle_errors(sess, attr.span, AttrError::MissingSince); |
8faf50e0 XL |
489 | continue |
490 | } | |
491 | } | |
492 | } | |
493 | _ => unreachable!() | |
494 | } | |
8faf50e0 XL |
495 | } |
496 | } | |
497 | ||
498 | // Merge the deprecation info into the stability info | |
499 | if let Some(rustc_depr) = rustc_depr { | |
500 | if let Some(ref mut stab) = stab { | |
501 | stab.rustc_depr = Some(rustc_depr); | |
502 | } else { | |
503 | span_err!(diagnostic, item_sp, E0549, | |
504 | "rustc_deprecated attribute must be paired with \ | |
505 | either stable or unstable attribute"); | |
506 | } | |
507 | } | |
508 | ||
509 | // Merge the const-unstable info into the stability info | |
b7449926 | 510 | if let Some(feature) = rustc_const_unstable { |
8faf50e0 | 511 | if let Some(ref mut stab) = stab { |
b7449926 | 512 | stab.const_stability = Some(feature); |
8faf50e0 XL |
513 | } else { |
514 | span_err!(diagnostic, item_sp, E0630, | |
515 | "rustc_const_unstable attribute must be paired with \ | |
516 | either stable or unstable attribute"); | |
517 | } | |
518 | } | |
519 | ||
0bf4aa26 | 520 | // Merge the const-unstable info into the stability info |
48663c56 | 521 | if promotable || allow_const_fn_ptr { |
0bf4aa26 | 522 | if let Some(ref mut stab) = stab { |
48663c56 XL |
523 | stab.promotable = promotable; |
524 | stab.allow_const_fn_ptr = allow_const_fn_ptr; | |
0bf4aa26 XL |
525 | } else { |
526 | span_err!(diagnostic, item_sp, E0717, | |
48663c56 XL |
527 | "rustc_promotable and rustc_allow_const_fn_ptr attributes \ |
528 | must be paired with either stable or unstable attribute"); | |
0bf4aa26 XL |
529 | } |
530 | } | |
531 | ||
8faf50e0 XL |
532 | stab |
533 | } | |
534 | ||
535 | pub fn find_crate_name(attrs: &[Attribute]) -> Option<Symbol> { | |
48663c56 | 536 | super::first_attr_value_str_by_name(attrs, sym::crate_name) |
8faf50e0 XL |
537 | } |
538 | ||
539 | /// Tests if a cfg-pattern matches the cfg set | |
540 | pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Features>) -> bool { | |
541 | eval_condition(cfg, sess, &mut |cfg| { | |
542 | if let (Some(feats), Some(gated_cfg)) = (features, GatedCfg::gate(cfg)) { | |
543 | gated_cfg.check_and_emit(sess, feats); | |
544 | } | |
545 | let error = |span, msg| { sess.span_diagnostic.span_err(span, msg); true }; | |
532ac7d7 XL |
546 | if cfg.path.segments.len() != 1 { |
547 | return error(cfg.path.span, "`cfg` predicate key must be an identifier"); | |
8faf50e0 | 548 | } |
e74abb32 | 549 | match &cfg.kind { |
8faf50e0 XL |
550 | MetaItemKind::List(..) => { |
551 | error(cfg.span, "unexpected parentheses after `cfg` predicate key") | |
552 | } | |
e74abb32 | 553 | MetaItemKind::NameValue(lit) if !lit.kind.is_str() => { |
a1dfa0c6 XL |
554 | handle_errors( |
555 | sess, | |
556 | lit.span, | |
557 | AttrError::UnsupportedLiteral( | |
558 | "literal in `cfg` predicate value must be a string", | |
e74abb32 | 559 | lit.kind.is_bytestr() |
a1dfa0c6 XL |
560 | ), |
561 | ); | |
562 | true | |
8faf50e0 XL |
563 | } |
564 | MetaItemKind::NameValue(..) | MetaItemKind::Word => { | |
9fa01778 XL |
565 | let ident = cfg.ident().expect("multi-segment cfg predicate"); |
566 | sess.config.contains(&(ident.name, cfg.value_str())) | |
8faf50e0 XL |
567 | } |
568 | } | |
569 | }) | |
570 | } | |
571 | ||
572 | /// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to | |
573 | /// evaluate individual items. | |
574 | pub fn eval_condition<F>(cfg: &ast::MetaItem, sess: &ParseSess, eval: &mut F) | |
575 | -> bool | |
576 | where F: FnMut(&ast::MetaItem) -> bool | |
577 | { | |
e74abb32 | 578 | match cfg.kind { |
8faf50e0 XL |
579 | ast::MetaItemKind::List(ref mis) => { |
580 | for mi in mis.iter() { | |
581 | if !mi.is_meta_item() { | |
a1dfa0c6 XL |
582 | handle_errors( |
583 | sess, | |
532ac7d7 | 584 | mi.span(), |
a1dfa0c6 XL |
585 | AttrError::UnsupportedLiteral( |
586 | "unsupported literal", | |
587 | false | |
588 | ), | |
589 | ); | |
8faf50e0 XL |
590 | return false; |
591 | } | |
592 | } | |
593 | ||
594 | // The unwraps below may look dangerous, but we've already asserted | |
595 | // that they won't fail with the loop above. | |
48663c56 XL |
596 | match cfg.name_or_empty() { |
597 | sym::any => mis.iter().any(|mi| { | |
8faf50e0 XL |
598 | eval_condition(mi.meta_item().unwrap(), sess, eval) |
599 | }), | |
48663c56 | 600 | sym::all => mis.iter().all(|mi| { |
8faf50e0 XL |
601 | eval_condition(mi.meta_item().unwrap(), sess, eval) |
602 | }), | |
48663c56 | 603 | sym::not => { |
8faf50e0 XL |
604 | if mis.len() != 1 { |
605 | span_err!(sess.span_diagnostic, cfg.span, E0536, "expected 1 cfg-pattern"); | |
606 | return false; | |
607 | } | |
608 | ||
609 | !eval_condition(mis[0].meta_item().unwrap(), sess, eval) | |
610 | }, | |
9fa01778 | 611 | _ => { |
e74abb32 XL |
612 | span_err!( |
613 | sess.span_diagnostic, cfg.span, E0537, | |
614 | "invalid predicate `{}`", | |
615 | pprust::path_to_string(&cfg.path) | |
616 | ); | |
8faf50e0 XL |
617 | false |
618 | } | |
619 | } | |
620 | }, | |
621 | ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => { | |
622 | eval(cfg) | |
623 | } | |
624 | } | |
625 | } | |
626 | ||
e74abb32 | 627 | #[derive(RustcEncodable, RustcDecodable, Clone)] |
8faf50e0 XL |
628 | pub struct Deprecation { |
629 | pub since: Option<Symbol>, | |
630 | pub note: Option<Symbol>, | |
631 | } | |
632 | ||
9fa01778 | 633 | /// Finds the deprecation attribute. `None` if none exists. |
a1dfa0c6 | 634 | pub fn find_deprecation(sess: &ParseSess, attrs: &[Attribute], |
8faf50e0 | 635 | item_sp: Span) -> Option<Deprecation> { |
a1dfa0c6 | 636 | find_deprecation_generic(sess, attrs.iter(), item_sp) |
8faf50e0 XL |
637 | } |
638 | ||
a1dfa0c6 | 639 | fn find_deprecation_generic<'a, I>(sess: &ParseSess, |
8faf50e0 XL |
640 | attrs_iter: I, |
641 | item_sp: Span) | |
642 | -> Option<Deprecation> | |
643 | where I: Iterator<Item = &'a Attribute> | |
644 | { | |
645 | let mut depr: Option<Deprecation> = None; | |
a1dfa0c6 | 646 | let diagnostic = &sess.span_diagnostic; |
8faf50e0 XL |
647 | |
648 | 'outer: for attr in attrs_iter { | |
48663c56 | 649 | if !attr.check_name(sym::deprecated) { |
9fa01778 | 650 | continue; |
8faf50e0 XL |
651 | } |
652 | ||
8faf50e0 XL |
653 | if depr.is_some() { |
654 | span_err!(diagnostic, item_sp, E0550, "multiple deprecated attributes"); | |
655 | break | |
656 | } | |
657 | ||
e74abb32 XL |
658 | let meta = match attr.meta() { |
659 | Some(meta) => meta, | |
660 | None => continue, | |
661 | }; | |
662 | depr = match &meta.kind { | |
9fa01778 XL |
663 | MetaItemKind::Word => Some(Deprecation { since: None, note: None }), |
664 | MetaItemKind::NameValue(..) => { | |
665 | meta.value_str().map(|note| { | |
666 | Deprecation { since: None, note: Some(note) } | |
667 | }) | |
668 | } | |
669 | MetaItemKind::List(list) => { | |
670 | let get = |meta: &MetaItem, item: &mut Option<Symbol>| { | |
671 | if item.is_some() { | |
a1dfa0c6 | 672 | handle_errors( |
e74abb32 XL |
673 | sess, |
674 | meta.span, | |
675 | AttrError::MultipleItem(pprust::path_to_string(&meta.path)), | |
a1dfa0c6 | 676 | ); |
9fa01778 | 677 | return false |
a1dfa0c6 | 678 | } |
9fa01778 XL |
679 | if let Some(v) = meta.value_str() { |
680 | *item = Some(v); | |
681 | true | |
682 | } else { | |
683 | if let Some(lit) = meta.name_value_literal() { | |
684 | handle_errors( | |
685 | sess, | |
686 | lit.span, | |
687 | AttrError::UnsupportedLiteral( | |
688 | "literal in `deprecated` \ | |
689 | value must be a string", | |
e74abb32 | 690 | lit.kind.is_bytestr() |
9fa01778 XL |
691 | ), |
692 | ); | |
693 | } else { | |
694 | span_err!(diagnostic, meta.span, E0551, "incorrect meta item"); | |
695 | } | |
a1dfa0c6 | 696 | |
9fa01778 XL |
697 | false |
698 | } | |
699 | }; | |
700 | ||
701 | let mut since = None; | |
702 | let mut note = None; | |
703 | for meta in list { | |
532ac7d7 XL |
704 | match meta { |
705 | NestedMetaItem::MetaItem(mi) => { | |
48663c56 XL |
706 | match mi.name_or_empty() { |
707 | sym::since => if !get(mi, &mut since) { continue 'outer }, | |
708 | sym::note => if !get(mi, &mut note) { continue 'outer }, | |
9fa01778 XL |
709 | _ => { |
710 | handle_errors( | |
711 | sess, | |
712 | meta.span(), | |
e74abb32 XL |
713 | AttrError::UnknownMetaItem( |
714 | pprust::path_to_string(&mi.path), | |
715 | &["since", "note"], | |
716 | ), | |
9fa01778 XL |
717 | ); |
718 | continue 'outer | |
719 | } | |
a1dfa0c6 | 720 | } |
8faf50e0 | 721 | } |
532ac7d7 | 722 | NestedMetaItem::Literal(lit) => { |
9fa01778 XL |
723 | handle_errors( |
724 | sess, | |
725 | lit.span, | |
726 | AttrError::UnsupportedLiteral( | |
727 | "item in `deprecated` must be a key/value pair", | |
728 | false, | |
729 | ), | |
730 | ); | |
731 | continue 'outer | |
732 | } | |
a1dfa0c6 | 733 | } |
8faf50e0 | 734 | } |
8faf50e0 | 735 | |
9fa01778 XL |
736 | Some(Deprecation { since, note }) |
737 | } | |
738 | }; | |
8faf50e0 XL |
739 | } |
740 | ||
741 | depr | |
742 | } | |
743 | ||
744 | #[derive(PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone)] | |
745 | pub enum ReprAttr { | |
746 | ReprInt(IntType), | |
747 | ReprC, | |
748 | ReprPacked(u32), | |
749 | ReprSimd, | |
750 | ReprTransparent, | |
751 | ReprAlign(u32), | |
752 | } | |
753 | ||
e74abb32 | 754 | #[derive(Eq, PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone)] |
8faf50e0 XL |
755 | pub enum IntType { |
756 | SignedInt(ast::IntTy), | |
757 | UnsignedInt(ast::UintTy) | |
758 | } | |
759 | ||
760 | impl IntType { | |
761 | #[inline] | |
762 | pub fn is_signed(self) -> bool { | |
9fa01778 | 763 | use IntType::*; |
8faf50e0 XL |
764 | |
765 | match self { | |
766 | SignedInt(..) => true, | |
767 | UnsignedInt(..) => false | |
768 | } | |
769 | } | |
770 | } | |
771 | ||
772 | /// Parse #[repr(...)] forms. | |
773 | /// | |
774 | /// Valid repr contents: any of the primitive integral type names (see | |
775 | /// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use | |
776 | /// the same discriminant size that the corresponding C enum would or C | |
777 | /// structure layout, `packed` to remove padding, and `transparent` to elegate representation | |
778 | /// concerns to the only non-ZST field. | |
a1dfa0c6 | 779 | pub fn find_repr_attrs(sess: &ParseSess, attr: &Attribute) -> Vec<ReprAttr> { |
9fa01778 | 780 | use ReprAttr::*; |
8faf50e0 XL |
781 | |
782 | let mut acc = Vec::new(); | |
a1dfa0c6 | 783 | let diagnostic = &sess.span_diagnostic; |
48663c56 | 784 | if attr.path == sym::repr { |
8faf50e0 XL |
785 | if let Some(items) = attr.meta_item_list() { |
786 | mark_used(attr); | |
787 | for item in items { | |
788 | if !item.is_meta_item() { | |
a1dfa0c6 XL |
789 | handle_errors( |
790 | sess, | |
532ac7d7 | 791 | item.span(), |
a1dfa0c6 XL |
792 | AttrError::UnsupportedLiteral( |
793 | "meta item in `repr` must be an identifier", | |
794 | false, | |
795 | ), | |
796 | ); | |
8faf50e0 XL |
797 | continue |
798 | } | |
799 | ||
800 | let mut recognised = false; | |
9fa01778 | 801 | if item.is_word() { |
48663c56 XL |
802 | let hint = match item.name_or_empty() { |
803 | sym::C => Some(ReprC), | |
804 | sym::packed => Some(ReprPacked(1)), | |
805 | sym::simd => Some(ReprSimd), | |
806 | sym::transparent => Some(ReprTransparent), | |
532ac7d7 | 807 | name => int_type_of_word(name).map(ReprInt), |
8faf50e0 XL |
808 | }; |
809 | ||
810 | if let Some(h) = hint { | |
811 | recognised = true; | |
812 | acc.push(h); | |
813 | } | |
814 | } else if let Some((name, value)) = item.name_value_literal() { | |
815 | let parse_alignment = |node: &ast::LitKind| -> Result<u32, &'static str> { | |
816 | if let ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed) = node { | |
817 | if literal.is_power_of_two() { | |
818 | // rustc::ty::layout::Align restricts align to <= 2^29 | |
819 | if *literal <= 1 << 29 { | |
820 | Ok(*literal as u32) | |
821 | } else { | |
822 | Err("larger than 2^29") | |
823 | } | |
824 | } else { | |
825 | Err("not a power of two") | |
826 | } | |
827 | } else { | |
828 | Err("not an unsuffixed integer") | |
829 | } | |
830 | }; | |
831 | ||
832 | let mut literal_error = None; | |
48663c56 | 833 | if name == sym::align { |
8faf50e0 | 834 | recognised = true; |
e74abb32 | 835 | match parse_alignment(&value.kind) { |
8faf50e0 XL |
836 | Ok(literal) => acc.push(ReprAlign(literal)), |
837 | Err(message) => literal_error = Some(message) | |
838 | }; | |
839 | } | |
48663c56 | 840 | else if name == sym::packed { |
8faf50e0 | 841 | recognised = true; |
e74abb32 | 842 | match parse_alignment(&value.kind) { |
8faf50e0 XL |
843 | Ok(literal) => acc.push(ReprPacked(literal)), |
844 | Err(message) => literal_error = Some(message) | |
845 | }; | |
846 | } | |
847 | if let Some(literal_error) = literal_error { | |
532ac7d7 | 848 | span_err!(diagnostic, item.span(), E0589, |
8faf50e0 XL |
849 | "invalid `repr(align)` attribute: {}", literal_error); |
850 | } | |
851 | } else { | |
852 | if let Some(meta_item) = item.meta_item() { | |
48663c56 | 853 | if meta_item.check_name(sym::align) { |
e74abb32 | 854 | if let MetaItemKind::NameValue(ref value) = meta_item.kind { |
8faf50e0 | 855 | recognised = true; |
532ac7d7 | 856 | let mut err = struct_span_err!(diagnostic, item.span(), E0693, |
8faf50e0 | 857 | "incorrect `repr(align)` attribute format"); |
e74abb32 | 858 | match value.kind { |
8faf50e0 | 859 | ast::LitKind::Int(int, ast::LitIntType::Unsuffixed) => { |
9fa01778 | 860 | err.span_suggestion( |
532ac7d7 | 861 | item.span(), |
8faf50e0 XL |
862 | "use parentheses instead", |
863 | format!("align({})", int), | |
864 | Applicability::MachineApplicable | |
865 | ); | |
866 | } | |
867 | ast::LitKind::Str(s, _) => { | |
9fa01778 | 868 | err.span_suggestion( |
532ac7d7 | 869 | item.span(), |
8faf50e0 XL |
870 | "use parentheses instead", |
871 | format!("align({})", s), | |
872 | Applicability::MachineApplicable | |
873 | ); | |
874 | } | |
875 | _ => {} | |
876 | } | |
877 | err.emit(); | |
878 | } | |
879 | } | |
880 | } | |
881 | } | |
882 | if !recognised { | |
883 | // Not a word we recognize | |
532ac7d7 | 884 | span_err!(diagnostic, item.span(), E0552, |
8faf50e0 XL |
885 | "unrecognized representation hint"); |
886 | } | |
887 | } | |
888 | } | |
889 | } | |
890 | acc | |
891 | } | |
892 | ||
48663c56 | 893 | fn int_type_of_word(s: Symbol) -> Option<IntType> { |
9fa01778 | 894 | use IntType::*; |
8faf50e0 XL |
895 | |
896 | match s { | |
48663c56 XL |
897 | sym::i8 => Some(SignedInt(ast::IntTy::I8)), |
898 | sym::u8 => Some(UnsignedInt(ast::UintTy::U8)), | |
899 | sym::i16 => Some(SignedInt(ast::IntTy::I16)), | |
900 | sym::u16 => Some(UnsignedInt(ast::UintTy::U16)), | |
901 | sym::i32 => Some(SignedInt(ast::IntTy::I32)), | |
902 | sym::u32 => Some(UnsignedInt(ast::UintTy::U32)), | |
903 | sym::i64 => Some(SignedInt(ast::IntTy::I64)), | |
904 | sym::u64 => Some(UnsignedInt(ast::UintTy::U64)), | |
905 | sym::i128 => Some(SignedInt(ast::IntTy::I128)), | |
906 | sym::u128 => Some(UnsignedInt(ast::UintTy::U128)), | |
907 | sym::isize => Some(SignedInt(ast::IntTy::Isize)), | |
908 | sym::usize => Some(UnsignedInt(ast::UintTy::Usize)), | |
8faf50e0 XL |
909 | _ => None |
910 | } | |
911 | } | |
416331ca XL |
912 | |
913 | pub enum TransparencyError { | |
914 | UnknownTransparency(Symbol, Span), | |
915 | MultipleTransparencyAttrs(Span, Span), | |
916 | } | |
917 | ||
918 | pub fn find_transparency( | |
919 | attrs: &[Attribute], is_legacy: bool | |
920 | ) -> (Transparency, Option<TransparencyError>) { | |
921 | let mut transparency = None; | |
922 | let mut error = None; | |
923 | for attr in attrs { | |
924 | if attr.check_name(sym::rustc_macro_transparency) { | |
925 | if let Some((_, old_span)) = transparency { | |
926 | error = Some(TransparencyError::MultipleTransparencyAttrs(old_span, attr.span)); | |
927 | break; | |
928 | } else if let Some(value) = attr.value_str() { | |
929 | transparency = Some((match &*value.as_str() { | |
930 | "transparent" => Transparency::Transparent, | |
931 | "semitransparent" => Transparency::SemiTransparent, | |
932 | "opaque" => Transparency::Opaque, | |
933 | _ => { | |
934 | error = Some(TransparencyError::UnknownTransparency(value, attr.span)); | |
935 | continue; | |
936 | } | |
937 | }, attr.span)); | |
938 | } | |
939 | } | |
940 | } | |
941 | let fallback = if is_legacy { Transparency::SemiTransparent } else { Transparency::Opaque }; | |
942 | (transparency.map_or(fallback, |t| t.0), error) | |
943 | } | |
944 | ||
e74abb32 | 945 | pub fn check_builtin_attribute( |
416331ca XL |
946 | sess: &ParseSess, attr: &ast::Attribute, name: Symbol, template: AttributeTemplate |
947 | ) { | |
948 | // Some special attributes like `cfg` must be checked | |
949 | // before the generic check, so we skip them here. | |
950 | let should_skip = |name| name == sym::cfg; | |
951 | // Some of previously accepted forms were used in practice, | |
952 | // report them as warnings for now. | |
953 | let should_warn = |name| name == sym::doc || name == sym::ignore || | |
954 | name == sym::inline || name == sym::link || | |
955 | name == sym::test || name == sym::bench; | |
956 | ||
957 | match attr.parse_meta(sess) { | |
e74abb32 | 958 | Ok(meta) => if !should_skip(name) && !template.compatible(&meta.kind) { |
416331ca XL |
959 | let error_msg = format!("malformed `{}` attribute input", name); |
960 | let mut msg = "attribute must be of the form ".to_owned(); | |
961 | let mut suggestions = vec![]; | |
962 | let mut first = true; | |
963 | if template.word { | |
964 | first = false; | |
965 | let code = format!("#[{}]", name); | |
966 | msg.push_str(&format!("`{}`", &code)); | |
967 | suggestions.push(code); | |
968 | } | |
969 | if let Some(descr) = template.list { | |
970 | if !first { | |
971 | msg.push_str(" or "); | |
972 | } | |
973 | first = false; | |
974 | let code = format!("#[{}({})]", name, descr); | |
975 | msg.push_str(&format!("`{}`", &code)); | |
976 | suggestions.push(code); | |
977 | } | |
978 | if let Some(descr) = template.name_value_str { | |
979 | if !first { | |
980 | msg.push_str(" or "); | |
981 | } | |
982 | let code = format!("#[{} = \"{}\"]", name, descr); | |
983 | msg.push_str(&format!("`{}`", &code)); | |
984 | suggestions.push(code); | |
985 | } | |
986 | if should_warn(name) { | |
987 | sess.buffer_lint( | |
988 | BufferedEarlyLintId::IllFormedAttributeInput, | |
989 | meta.span, | |
990 | ast::CRATE_NODE_ID, | |
991 | &msg, | |
992 | ); | |
993 | } else { | |
994 | sess.span_diagnostic.struct_span_err(meta.span, &error_msg) | |
995 | .span_suggestions( | |
996 | meta.span, | |
997 | if suggestions.len() == 1 { | |
998 | "must be of the form" | |
999 | } else { | |
1000 | "the following are the possible correct uses" | |
1001 | }, | |
1002 | suggestions.into_iter(), | |
1003 | Applicability::HasPlaceholders, | |
1004 | ).emit(); | |
1005 | } | |
1006 | } | |
1007 | Err(mut err) => err.emit(), | |
1008 | } | |
1009 | } |