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