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