1 //! Parsing and validation of builtin attributes
3 use crate::ast::{self, Attribute, MetaItem, NestedMetaItem};
4 use crate::feature_gate::{Features, GatedCfg};
5 use crate::parse::ParseSess;
7 use errors::{Applicability, Handler};
8 use syntax_pos::{symbol::Symbol, Span};
10 use super::{mark_used, MetaItemKind};
14 UnknownMetaItem(String, &'static [&'static str]),
17 MultipleStabilityLevels,
18 UnsupportedLiteral(&'static str, /* is_bytestr */ bool),
21 fn handle_errors(sess: &ParseSess, span: Span, error: AttrError) {
22 let diag = &sess.span_diagnostic;
24 AttrError::MultipleItem(item) => span_err!(diag, span, E0538,
25 "multiple '{}' items", item),
26 AttrError::UnknownMetaItem(item, expected) => {
27 let expected = expected
29 .map(|name| format!("`{}`", name))
31 struct_span_err!(diag, span, E0541, "unknown meta item '{}'", item)
32 .span_label(span, format!("expected one of {}", expected.join(", ")))
35 AttrError::MissingSince => span_err!(diag, span, E0542, "missing 'since'"),
36 AttrError::MissingFeature => span_err!(diag, span, E0546, "missing 'feature'"),
37 AttrError::MultipleStabilityLevels => span_err!(diag, span, E0544,
38 "multiple stability levels"),
39 AttrError::UnsupportedLiteral(
43 let mut err = struct_span_err!(diag, span, E0565, "{}", msg);
45 if let Ok(lint_str) = sess.source_map().span_to_snippet(span) {
48 "consider removing the prefix",
49 format!("{}", &lint_str[1..]),
50 Applicability::MaybeIncorrect,
59 #[derive(Copy, Clone, Hash, PartialEq, RustcEncodable, RustcDecodable)]
67 #[derive(Copy, Clone, Hash, PartialEq, RustcEncodable, RustcDecodable)]
68 pub enum OptimizeAttr {
74 #[derive(Copy, Clone, PartialEq)]
80 /// Determine what `#[unwind]` attribute is present in `attrs`, if any.
81 pub fn find_unwind_attr(diagnostic: Option<&Handler>, attrs: &[Attribute]) -> Option<UnwindAttr> {
82 attrs.iter().fold(None, |ia, attr| {
83 if attr.check_name("unwind") {
84 if let Some(meta) = attr.meta() {
85 if let MetaItemKind::List(items) = meta.node {
87 if items[0].check_name("allowed") {
88 return Some(UnwindAttr::Allowed);
89 } else if items[0].check_name("aborts") {
90 return Some(UnwindAttr::Aborts);
95 span_err!(d, attr.span, E0633, "malformed `#[unwind]` attribute");
105 /// Represents the #[stable], #[unstable], #[rustc_{deprecated,const_unstable}] attributes.
106 #[derive(RustcEncodable, RustcDecodable, Clone, Debug, PartialEq, Eq, Hash)]
107 pub struct Stability {
108 pub level: StabilityLevel,
110 pub rustc_depr: Option<RustcDeprecation>,
111 /// `None` means the function is stable but needs to be a stable const fn, too
112 /// `Some` contains the feature gate required to be able to use the function
114 pub const_stability: Option<Symbol>,
115 /// whether the function has a `#[rustc_promotable]` attribute
116 pub promotable: bool,
119 /// The available stability levels.
120 #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Eq, Hash)]
121 pub enum StabilityLevel {
122 // Reason for the current stability level and the relevant rust-lang issue
123 Unstable { reason: Option<Symbol>, issue: u32 },
124 Stable { since: Symbol },
127 impl StabilityLevel {
128 pub fn is_unstable(&self) -> bool {
129 if let StabilityLevel::Unstable {..} = *self {
135 pub fn is_stable(&self) -> bool {
136 if let StabilityLevel::Stable {..} = *self {
144 #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Eq, Hash)]
145 pub struct RustcDeprecation {
148 /// A text snippet used to completely replace any use of the deprecated item in an expression.
149 pub suggestion: Option<Symbol>,
152 /// Checks if `attrs` contains an attribute like `#![feature(feature_name)]`.
153 /// This will not perform any "sanity checks" on the form of the attributes.
154 pub fn contains_feature_attr(attrs: &[Attribute], feature_name: &str) -> bool {
155 attrs.iter().any(|item| {
156 item.check_name("feature") &&
157 item.meta_item_list().map(|list| {
158 list.iter().any(|mi| mi.is_word() && mi.check_name(feature_name))
163 /// Finds the first stability attribute. `None` if none exists.
164 pub fn find_stability(sess: &ParseSess, attrs: &[Attribute],
165 item_sp: Span) -> Option<Stability> {
166 find_stability_generic(sess, attrs.iter(), item_sp)
169 fn find_stability_generic<'a, I>(sess: &ParseSess,
173 where I: Iterator<Item = &'a Attribute>
175 use StabilityLevel::*;
177 let mut stab: Option<Stability> = None;
178 let mut rustc_depr: Option<RustcDeprecation> = None;
179 let mut rustc_const_unstable: Option<Symbol> = None;
180 let mut promotable = false;
181 let diagnostic = &sess.span_diagnostic;
183 'outer: for attr in attrs_iter {
186 "rustc_const_unstable",
190 ].iter().any(|&s| attr.path == s) {
191 continue // not a stability level
196 let meta = attr.meta();
198 if attr.path == "rustc_promotable" {
201 // attributes with data
202 else if let Some(MetaItem { node: MetaItemKind::List(ref metas), .. }) = meta {
203 let meta = meta.as_ref().unwrap();
204 let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
206 handle_errors(sess, meta.span, AttrError::MultipleItem(meta.path.to_string()));
209 if let Some(v) = meta.value_str() {
213 span_err!(diagnostic, meta.span, E0539, "incorrect meta item");
218 macro_rules! get_meta {
219 ($($name:ident),+) => {
221 let mut $name = None;
224 if let Some(mi) = meta.meta_item() {
225 match mi.name_or_empty().get() {
228 => if !get(mi, &mut $name) { continue 'outer },
231 let expected = &[ $( stringify!($name) ),+ ];
235 AttrError::UnknownMetaItem(mi.path.to_string(), expected),
244 AttrError::UnsupportedLiteral(
245 "unsupported literal",
255 match meta.name_or_empty().get() {
256 "rustc_deprecated" => {
257 if rustc_depr.is_some() {
258 span_err!(diagnostic, item_sp, E0540,
259 "multiple rustc_deprecated attributes");
263 get_meta!(since, reason, suggestion);
265 match (since, reason) {
266 (Some(since), Some(reason)) => {
267 rustc_depr = Some(RustcDeprecation {
274 handle_errors(sess, attr.span, AttrError::MissingSince);
278 span_err!(diagnostic, attr.span, E0543, "missing 'reason'");
283 "rustc_const_unstable" => {
284 if rustc_const_unstable.is_some() {
285 span_err!(diagnostic, item_sp, E0553,
286 "multiple rustc_const_unstable attributes");
291 if let Some(feature) = feature {
292 rustc_const_unstable = Some(feature);
294 span_err!(diagnostic, attr.span, E0629, "missing 'feature'");
300 handle_errors(sess, attr.span, AttrError::MultipleStabilityLevels);
304 let mut feature = None;
305 let mut reason = None;
306 let mut issue = None;
308 if let Some(mi) = meta.meta_item() {
309 match mi.name_or_empty().get() {
310 "feature" => if !get(mi, &mut feature) { continue 'outer },
311 "reason" => if !get(mi, &mut reason) { continue 'outer },
312 "issue" => if !get(mi, &mut issue) { continue 'outer },
317 AttrError::UnknownMetaItem(
319 &["feature", "reason", "issue"]
329 AttrError::UnsupportedLiteral(
330 "unsupported literal",
338 match (feature, reason, issue) {
339 (Some(feature), reason, Some(issue)) => {
340 stab = Some(Stability {
344 if let Ok(issue) = issue.as_str().parse() {
347 span_err!(diagnostic, attr.span, E0545,
348 "incorrect 'issue'");
355 const_stability: None,
360 handle_errors(sess, attr.span, AttrError::MissingFeature);
364 span_err!(diagnostic, attr.span, E0547, "missing 'issue'");
371 handle_errors(sess, attr.span, AttrError::MultipleStabilityLevels);
375 let mut feature = None;
376 let mut since = None;
379 NestedMetaItem::MetaItem(mi) => {
380 match mi.name_or_empty().get() {
382 if !get(mi, &mut feature) { continue 'outer },
384 if !get(mi, &mut since) { continue 'outer },
389 AttrError::UnknownMetaItem(
390 mi.path.to_string(), &["since", "note"],
397 NestedMetaItem::Literal(lit) => {
401 AttrError::UnsupportedLiteral(
402 "unsupported literal",
411 match (feature, since) {
412 (Some(feature), Some(since)) => {
413 stab = Some(Stability {
419 const_stability: None,
424 handle_errors(sess, attr.span, AttrError::MissingFeature);
428 handle_errors(sess, attr.span, AttrError::MissingSince);
438 // Merge the deprecation info into the stability info
439 if let Some(rustc_depr) = rustc_depr {
440 if let Some(ref mut stab) = stab {
441 stab.rustc_depr = Some(rustc_depr);
443 span_err!(diagnostic, item_sp, E0549,
444 "rustc_deprecated attribute must be paired with \
445 either stable or unstable attribute");
449 // Merge the const-unstable info into the stability info
450 if let Some(feature) = rustc_const_unstable {
451 if let Some(ref mut stab) = stab {
452 stab.const_stability = Some(feature);
454 span_err!(diagnostic, item_sp, E0630,
455 "rustc_const_unstable attribute must be paired with \
456 either stable or unstable attribute");
460 // Merge the const-unstable info into the stability info
462 if let Some(ref mut stab) = stab {
463 stab.promotable = true;
465 span_err!(diagnostic, item_sp, E0717,
466 "rustc_promotable attribute must be paired with \
467 either stable or unstable attribute");
474 pub fn find_crate_name(attrs: &[Attribute]) -> Option<Symbol> {
475 super::first_attr_value_str_by_name(attrs, "crate_name")
478 /// Tests if a cfg-pattern matches the cfg set
479 pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Features>) -> bool {
480 eval_condition(cfg, sess, &mut |cfg| {
481 if let (Some(feats), Some(gated_cfg)) = (features, GatedCfg::gate(cfg)) {
482 gated_cfg.check_and_emit(sess, feats);
484 let error = |span, msg| { sess.span_diagnostic.span_err(span, msg); true };
485 if cfg.path.segments.len() != 1 {
486 return error(cfg.path.span, "`cfg` predicate key must be an identifier");
489 MetaItemKind::List(..) => {
490 error(cfg.span, "unexpected parentheses after `cfg` predicate key")
492 MetaItemKind::NameValue(lit) if !lit.node.is_str() => {
496 AttrError::UnsupportedLiteral(
497 "literal in `cfg` predicate value must be a string",
498 lit.node.is_bytestr()
503 MetaItemKind::NameValue(..) | MetaItemKind::Word => {
504 let ident = cfg.ident().expect("multi-segment cfg predicate");
505 sess.config.contains(&(ident.name, cfg.value_str()))
511 /// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to
512 /// evaluate individual items.
513 pub fn eval_condition<F>(cfg: &ast::MetaItem, sess: &ParseSess, eval: &mut F)
515 where F: FnMut(&ast::MetaItem) -> bool
518 ast::MetaItemKind::List(ref mis) => {
519 for mi in mis.iter() {
520 if !mi.is_meta_item() {
524 AttrError::UnsupportedLiteral(
525 "unsupported literal",
533 // The unwraps below may look dangerous, but we've already asserted
534 // that they won't fail with the loop above.
535 match cfg.name_or_empty().get() {
536 "any" => mis.iter().any(|mi| {
537 eval_condition(mi.meta_item().unwrap(), sess, eval)
539 "all" => mis.iter().all(|mi| {
540 eval_condition(mi.meta_item().unwrap(), sess, eval)
544 span_err!(sess.span_diagnostic, cfg.span, E0536, "expected 1 cfg-pattern");
548 !eval_condition(mis[0].meta_item().unwrap(), sess, eval)
551 span_err!(sess.span_diagnostic, cfg.span, E0537,
552 "invalid predicate `{}`", cfg.path);
557 ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => {
564 #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Eq, Hash)]
565 pub struct Deprecation {
566 pub since: Option<Symbol>,
567 pub note: Option<Symbol>,
570 /// Finds the deprecation attribute. `None` if none exists.
571 pub fn find_deprecation(sess: &ParseSess, attrs: &[Attribute],
572 item_sp: Span) -> Option<Deprecation> {
573 find_deprecation_generic(sess, attrs.iter(), item_sp)
576 fn find_deprecation_generic<'a, I>(sess: &ParseSess,
579 -> Option<Deprecation>
580 where I: Iterator<Item = &'a Attribute>
582 let mut depr: Option<Deprecation> = None;
583 let diagnostic = &sess.span_diagnostic;
585 'outer: for attr in attrs_iter {
586 if !attr.check_name("deprecated") {
591 span_err!(diagnostic, item_sp, E0550, "multiple deprecated attributes");
595 let meta = attr.meta().unwrap();
596 depr = match &meta.node {
597 MetaItemKind::Word => Some(Deprecation { since: None, note: None }),
598 MetaItemKind::NameValue(..) => {
599 meta.value_str().map(|note| {
600 Deprecation { since: None, note: Some(note) }
603 MetaItemKind::List(list) => {
604 let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
607 sess, meta.span, AttrError::MultipleItem(meta.path.to_string())
611 if let Some(v) = meta.value_str() {
615 if let Some(lit) = meta.name_value_literal() {
619 AttrError::UnsupportedLiteral(
620 "literal in `deprecated` \
621 value must be a string",
622 lit.node.is_bytestr()
626 span_err!(diagnostic, meta.span, E0551, "incorrect meta item");
633 let mut since = None;
637 NestedMetaItem::MetaItem(mi) => {
638 match mi.name_or_empty().get() {
639 "since" => if !get(mi, &mut since) { continue 'outer },
640 "note" => if !get(mi, &mut note) { continue 'outer },
645 AttrError::UnknownMetaItem(mi.path.to_string(),
652 NestedMetaItem::Literal(lit) => {
656 AttrError::UnsupportedLiteral(
657 "item in `deprecated` must be a key/value pair",
666 Some(Deprecation { since, note })
674 #[derive(PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone)]
684 #[derive(Eq, Hash, PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone)]
686 SignedInt(ast::IntTy),
687 UnsignedInt(ast::UintTy)
692 pub fn is_signed(self) -> bool {
696 SignedInt(..) => true,
697 UnsignedInt(..) => false
702 /// Parse #[repr(...)] forms.
704 /// Valid repr contents: any of the primitive integral type names (see
705 /// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use
706 /// the same discriminant size that the corresponding C enum would or C
707 /// structure layout, `packed` to remove padding, and `transparent` to elegate representation
708 /// concerns to the only non-ZST field.
709 pub fn find_repr_attrs(sess: &ParseSess, attr: &Attribute) -> Vec<ReprAttr> {
712 let mut acc = Vec::new();
713 let diagnostic = &sess.span_diagnostic;
714 if attr.path == "repr" {
715 if let Some(items) = attr.meta_item_list() {
718 if !item.is_meta_item() {
722 AttrError::UnsupportedLiteral(
723 "meta item in `repr` must be an identifier",
730 let mut recognised = false;
732 let hint = match item.name_or_empty().get() {
734 "packed" => Some(ReprPacked(1)),
735 "simd" => Some(ReprSimd),
736 "transparent" => Some(ReprTransparent),
737 name => int_type_of_word(name).map(ReprInt),
740 if let Some(h) = hint {
744 } else if let Some((name, value)) = item.name_value_literal() {
745 let parse_alignment = |node: &ast::LitKind| -> Result<u32, &'static str> {
746 if let ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed) = node {
747 if literal.is_power_of_two() {
748 // rustc::ty::layout::Align restricts align to <= 2^29
749 if *literal <= 1 << 29 {
752 Err("larger than 2^29")
755 Err("not a power of two")
758 Err("not an unsuffixed integer")
762 let mut literal_error = None;
765 match parse_alignment(&value.node) {
766 Ok(literal) => acc.push(ReprAlign(literal)),
767 Err(message) => literal_error = Some(message)
770 else if name == "packed" {
772 match parse_alignment(&value.node) {
773 Ok(literal) => acc.push(ReprPacked(literal)),
774 Err(message) => literal_error = Some(message)
777 if let Some(literal_error) = literal_error {
778 span_err!(diagnostic, item.span(), E0589,
779 "invalid `repr(align)` attribute: {}", literal_error);
782 if let Some(meta_item) = item.meta_item() {
783 if meta_item.check_name("align") {
784 if let MetaItemKind::NameValue(ref value) = meta_item.node {
786 let mut err = struct_span_err!(diagnostic, item.span(), E0693,
787 "incorrect `repr(align)` attribute format");
789 ast::LitKind::Int(int, ast::LitIntType::Unsuffixed) => {
792 "use parentheses instead",
793 format!("align({})", int),
794 Applicability::MachineApplicable
797 ast::LitKind::Str(s, _) => {
800 "use parentheses instead",
801 format!("align({})", s),
802 Applicability::MachineApplicable
813 // Not a word we recognize
814 span_err!(diagnostic, item.span(), E0552,
815 "unrecognized representation hint");
823 fn int_type_of_word(s: &str) -> Option<IntType> {
827 "i8" => Some(SignedInt(ast::IntTy::I8)),
828 "u8" => Some(UnsignedInt(ast::UintTy::U8)),
829 "i16" => Some(SignedInt(ast::IntTy::I16)),
830 "u16" => Some(UnsignedInt(ast::UintTy::U16)),
831 "i32" => Some(SignedInt(ast::IntTy::I32)),
832 "u32" => Some(UnsignedInt(ast::UintTy::U32)),
833 "i64" => Some(SignedInt(ast::IntTy::I64)),
834 "u64" => Some(UnsignedInt(ast::UintTy::U64)),
835 "i128" => Some(SignedInt(ast::IntTy::I128)),
836 "u128" => Some(UnsignedInt(ast::UintTy::U128)),
837 "isize" => Some(SignedInt(ast::IntTy::Isize)),
838 "usize" => Some(UnsignedInt(ast::UintTy::Usize)),