]> git.proxmox.com Git - rustc.git/blame - src/librustc_attr/builtin.rs
New upstream version 1.43.0+dfsg1
[rustc.git] / src / librustc_attr / builtin.rs
CommitLineData
8faf50e0
XL
1//! Parsing and validation of builtin attributes
2
74b04a01 3use super::{find_by_name, mark_used};
9fa01778 4
74b04a01
XL
5use rustc_ast::ast::{self, Attribute, MetaItem, MetaItemKind, NestedMetaItem};
6use rustc_ast_pretty::pprust;
dfeec247
XL
7use rustc_errors::{struct_span_err, Applicability, Handler};
8use rustc_feature::{find_gated_cfg, is_builtin_attr_name, Features, GatedCfg};
60c5eb7d 9use rustc_macros::HashStable_Generic;
74b04a01 10use rustc_session::parse::{feature_err, ParseSess};
dfeec247
XL
11use rustc_span::hygiene::Transparency;
12use rustc_span::{symbol::sym, symbol::Symbol, Span};
13use std::num::NonZeroU32;
60c5eb7d
XL
14
15pub 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
19enum 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
28fn 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
67pub enum InlineAttr {
68 None,
69 Hint,
70 Always,
71 Never,
72}
73
e74abb32 74#[derive(Clone, RustcEncodable, RustcDecodable)]
9fa01778
XL
75pub enum OptimizeAttr {
76 None,
77 Speed,
78 Size,
79}
80
8faf50e0
XL
81#[derive(Copy, Clone, PartialEq)]
82pub enum UnwindAttr {
83 Allowed,
84 Aborts,
85}
86
87/// Determine what `#[unwind]` attribute is present in `attrs`, if any.
88pub 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
134pub 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
152pub 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
174pub 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
180impl 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
201pub 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 210pub 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
222pub 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
230fn find_stability_generic<'a, I>(
231 sess: &ParseSess,
232 attrs_iter: I,
233 item_sp: Span,
234) -> (Option<Stability>, Option<ConstStability>)
235where
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
599pub 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
604pub 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
640fn 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
650pub 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
709pub 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
715pub 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
723fn find_deprecation_generic<'a, I>(
724 sess: &ParseSess,
725 attrs_iter: I,
726 item_sp: Span,
727) -> Option<Deprecation>
728where
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)]
836pub 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
847pub enum IntType {
848 SignedInt(ast::IntTy),
dfeec247 849 UnsignedInt(ast::UintTy),
8faf50e0
XL
850}
851
852impl 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 871pub 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 1000fn 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
1020pub enum TransparencyError {
1021 UnknownTransparency(Symbol, Span),
1022 MultipleTransparencyAttrs(Span, Span),
1023}
1024
1025pub 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
1056pub 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}