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