1 //! Parsing and validation of builtin attributes
3 use ast
::{self, Attribute, MetaItem, Name, NestedMetaItemKind}
;
4 use errors
::{Applicability, Handler}
;
5 use feature_gate
::{Features, GatedCfg}
;
7 use syntax_pos
::{symbol::Symbol, Span}
;
9 use super::{list_contains_name, mark_used, MetaItemKind}
;
13 UnknownMetaItem(Name
, &'
static [&'
static str]),
16 MultipleStabilityLevels
,
17 UnsupportedLiteral(&'
static str, /* is_bytestr */ bool
),
20 fn handle_errors(sess
: &ParseSess
, span
: Span
, error
: AttrError
) {
21 let diag
= &sess
.span_diagnostic
;
23 AttrError
::MultipleItem(item
) => span_err
!(diag
, span
, E0538
,
24 "multiple '{}' items", item
),
25 AttrError
::UnknownMetaItem(item
, expected
) => {
26 let expected
= expected
28 .map(|name
| format
!("`{}`", name
))
30 struct_span_err
!(diag
, span
, E0541
, "unknown meta item '{}'", item
)
31 .span_label(span
, format
!("expected one of {}", expected
.join(", ")))
34 AttrError
::MissingSince
=> span_err
!(diag
, span
, E0542
, "missing 'since'"),
35 AttrError
::MissingFeature
=> span_err
!(diag
, span
, E0546
, "missing 'feature'"),
36 AttrError
::MultipleStabilityLevels
=> span_err
!(diag
, span
, E0544
,
37 "multiple stability levels"),
38 AttrError
::UnsupportedLiteral(
42 let mut err
= struct_span_err
!(diag
, span
, E0565
, "{}", msg
);
44 if let Ok(lint_str
) = sess
.source_map().span_to_snippet(span
) {
45 err
.span_suggestion_with_applicability(
47 "consider removing the prefix",
48 format
!("{}", &lint_str
[1..]),
49 Applicability
::MaybeIncorrect
,
58 #[derive(Copy, Clone, Hash, PartialEq, RustcEncodable, RustcDecodable)]
66 #[derive(Copy, Clone, PartialEq)]
72 /// Determine what `#[unwind]` attribute is present in `attrs`, if any.
73 pub fn find_unwind_attr(diagnostic
: Option
<&Handler
>, attrs
: &[Attribute
]) -> Option
<UnwindAttr
> {
74 let syntax_error
= |attr
: &Attribute
| {
77 span_err
!(d
, attr
.span
, E0633
, "malformed `#[unwind]` attribute");
82 attrs
.iter().fold(None
, |ia
, attr
| {
83 if attr
.path
!= "unwind" {
86 let meta
= match attr
.meta() {
87 Some(meta
) => meta
.node
,
91 MetaItemKind
::Word
=> {
94 MetaItemKind
::List(ref items
) => {
98 } else if list_contains_name(&items
[..], "allowed") {
99 Some(UnwindAttr
::Allowed
)
100 } else if list_contains_name(&items
[..], "aborts") {
101 Some(UnwindAttr
::Aborts
)
111 /// Represents the #[stable], #[unstable], #[rustc_{deprecated,const_unstable}] attributes.
112 #[derive(RustcEncodable, RustcDecodable, Clone, Debug, PartialEq, Eq, Hash)]
113 pub struct Stability
{
114 pub level
: StabilityLevel
,
116 pub rustc_depr
: Option
<RustcDeprecation
>,
117 /// `None` means the function is stable but needs to be a stable const fn, too
118 /// `Some` contains the feature gate required to be able to use the function
120 pub const_stability
: Option
<Symbol
>,
121 /// whether the function has a `#[rustc_promotable]` attribute
122 pub promotable
: bool
,
125 /// The available stability levels.
126 #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Eq, Hash)]
127 pub enum StabilityLevel
{
128 // Reason for the current stability level and the relevant rust-lang issue
129 Unstable { reason: Option<Symbol>, issue: u32 }
,
130 Stable { since: Symbol }
,
133 impl StabilityLevel
{
134 pub fn is_unstable(&self) -> bool
{
135 if let StabilityLevel
::Unstable {..}
= *self {
141 pub fn is_stable(&self) -> bool
{
142 if let StabilityLevel
::Stable {..}
= *self {
150 #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Eq, Hash)]
151 pub struct RustcDeprecation
{
156 /// Check if `attrs` contains an attribute like `#![feature(feature_name)]`.
157 /// This will not perform any "sanity checks" on the form of the attributes.
158 pub fn contains_feature_attr(attrs
: &[Attribute
], feature_name
: &str) -> bool
{
159 attrs
.iter().any(|item
| {
160 item
.check_name("feature") &&
161 item
.meta_item_list().map(|list
| {
162 list
.iter().any(|mi
| {
163 mi
.word().map(|w
| w
.name() == feature_name
)
170 /// Find the first stability attribute. `None` if none exists.
171 pub fn find_stability(sess
: &ParseSess
, attrs
: &[Attribute
],
172 item_sp
: Span
) -> Option
<Stability
> {
173 find_stability_generic(sess
, attrs
.iter(), item_sp
)
176 fn find_stability_generic
<'a
, I
>(sess
: &ParseSess
,
180 where I
: Iterator
<Item
= &'a Attribute
>
182 use self::StabilityLevel
::*;
184 let mut stab
: Option
<Stability
> = None
;
185 let mut rustc_depr
: Option
<RustcDeprecation
> = None
;
186 let mut rustc_const_unstable
: Option
<Symbol
> = None
;
187 let mut promotable
= false;
188 let diagnostic
= &sess
.span_diagnostic
;
190 'outer
: for attr
in attrs_iter
{
193 "rustc_const_unstable",
197 ].iter().any(|&s
| attr
.path
== s
) {
198 continue // not a stability level
203 let meta
= attr
.meta();
205 if attr
.path
== "rustc_promotable" {
208 // attributes with data
209 else if let Some(MetaItem { node: MetaItemKind::List(ref metas), .. }
) = meta
{
210 let meta
= meta
.as_ref().unwrap();
211 let get
= |meta
: &MetaItem
, item
: &mut Option
<Symbol
>| {
213 handle_errors(sess
, meta
.span
, AttrError
::MultipleItem(meta
.name()));
216 if let Some(v
) = meta
.value_str() {
220 span_err
!(diagnostic
, meta
.span
, E0539
, "incorrect meta item");
225 macro_rules
! get_meta
{
226 ($
($name
:ident
),+) => {
228 let mut $name
= None
;
231 if let Some(mi
) = meta
.meta_item() {
232 match &*mi
.name().as_str() {
235 => if !get(mi
, &mut $name
) { continue 'outer }
,
238 let expected
= &[ $
( stringify
!($name
) ),+ ];
242 AttrError
::UnknownMetaItem(mi
.name(), expected
),
251 AttrError
::UnsupportedLiteral(
252 "unsupported literal",
262 match &*meta
.name().as_str() {
263 "rustc_deprecated" => {
264 if rustc_depr
.is_some() {
265 span_err
!(diagnostic
, item_sp
, E0540
,
266 "multiple rustc_deprecated attributes");
270 get_meta
!(since
, reason
);
272 match (since
, reason
) {
273 (Some(since
), Some(reason
)) => {
274 rustc_depr
= Some(RustcDeprecation
{
280 handle_errors(sess
, attr
.span(), AttrError
::MissingSince
);
284 span_err
!(diagnostic
, attr
.span(), E0543
, "missing 'reason'");
289 "rustc_const_unstable" => {
290 if rustc_const_unstable
.is_some() {
291 span_err
!(diagnostic
, item_sp
, E0553
,
292 "multiple rustc_const_unstable attributes");
297 if let Some(feature
) = feature
{
298 rustc_const_unstable
= Some(feature
);
300 span_err
!(diagnostic
, attr
.span(), E0629
, "missing 'feature'");
306 handle_errors(sess
, attr
.span(), AttrError
::MultipleStabilityLevels
);
310 let mut feature
= None
;
311 let mut reason
= None
;
312 let mut issue
= None
;
314 if let Some(mi
) = meta
.meta_item() {
315 match &*mi
.name().as_str() {
316 "feature" => if !get(mi
, &mut feature
) { continue 'outer }
,
317 "reason" => if !get(mi
, &mut reason
) { continue 'outer }
,
318 "issue" => if !get(mi
, &mut issue
) { continue 'outer }
,
323 AttrError
::UnknownMetaItem(
325 &["feature", "reason", "issue"]
335 AttrError
::UnsupportedLiteral(
336 "unsupported literal",
344 match (feature
, reason
, issue
) {
345 (Some(feature
), reason
, Some(issue
)) => {
346 stab
= Some(Stability
{
350 if let Ok(issue
) = issue
.as_str().parse() {
353 span_err
!(diagnostic
, attr
.span(), E0545
,
354 "incorrect 'issue'");
361 const_stability
: None
,
366 handle_errors(sess
, attr
.span(), AttrError
::MissingFeature
);
370 span_err
!(diagnostic
, attr
.span(), E0547
, "missing 'issue'");
377 handle_errors(sess
, attr
.span(), AttrError
::MultipleStabilityLevels
);
381 let mut feature
= None
;
382 let mut since
= None
;
385 NestedMetaItemKind
::MetaItem(mi
) => {
386 match &*mi
.name().as_str() {
387 "feature" => if !get(mi
, &mut feature
) { continue 'outer }
,
388 "since" => if !get(mi
, &mut since
) { continue 'outer }
,
393 AttrError
::UnknownMetaItem(
394 mi
.name(), &["since", "note"],
401 NestedMetaItemKind
::Literal(lit
) => {
405 AttrError
::UnsupportedLiteral(
406 "unsupported literal",
415 match (feature
, since
) {
416 (Some(feature
), Some(since
)) => {
417 stab
= Some(Stability
{
423 const_stability
: None
,
428 handle_errors(sess
, attr
.span(), AttrError
::MissingFeature
);
432 handle_errors(sess
, attr
.span(), AttrError
::MissingSince
);
440 span_err
!(diagnostic
, attr
.span(), E0548
, "incorrect stability attribute type");
445 // Merge the deprecation info into the stability info
446 if let Some(rustc_depr
) = rustc_depr
{
447 if let Some(ref mut stab
) = stab
{
448 stab
.rustc_depr
= Some(rustc_depr
);
450 span_err
!(diagnostic
, item_sp
, E0549
,
451 "rustc_deprecated attribute must be paired with \
452 either stable or unstable attribute");
456 // Merge the const-unstable info into the stability info
457 if let Some(feature
) = rustc_const_unstable
{
458 if let Some(ref mut stab
) = stab
{
459 stab
.const_stability
= Some(feature
);
461 span_err
!(diagnostic
, item_sp
, E0630
,
462 "rustc_const_unstable attribute must be paired with \
463 either stable or unstable attribute");
467 // Merge the const-unstable info into the stability info
469 if let Some(ref mut stab
) = stab
{
470 stab
.promotable
= true;
472 span_err
!(diagnostic
, item_sp
, E0717
,
473 "rustc_promotable attribute must be paired with \
474 either stable or unstable attribute");
481 pub fn find_crate_name(attrs
: &[Attribute
]) -> Option
<Symbol
> {
482 super::first_attr_value_str_by_name(attrs
, "crate_name")
485 /// Tests if a cfg-pattern matches the cfg set
486 pub fn cfg_matches(cfg
: &ast
::MetaItem
, sess
: &ParseSess
, features
: Option
<&Features
>) -> bool
{
487 eval_condition(cfg
, sess
, &mut |cfg
| {
488 if let (Some(feats
), Some(gated_cfg
)) = (features
, GatedCfg
::gate(cfg
)) {
489 gated_cfg
.check_and_emit(sess
, feats
);
491 let error
= |span
, msg
| { sess.span_diagnostic.span_err(span, msg); true }
;
492 if cfg
.ident
.segments
.len() != 1 {
493 return error(cfg
.ident
.span
, "`cfg` predicate key must be an identifier");
496 MetaItemKind
::List(..) => {
497 error(cfg
.span
, "unexpected parentheses after `cfg` predicate key")
499 MetaItemKind
::NameValue(lit
) if !lit
.node
.is_str() => {
503 AttrError
::UnsupportedLiteral(
504 "literal in `cfg` predicate value must be a string",
505 lit
.node
.is_bytestr()
510 MetaItemKind
::NameValue(..) | MetaItemKind
::Word
=> {
511 sess
.config
.contains(&(cfg
.name(), cfg
.value_str()))
517 /// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to
518 /// evaluate individual items.
519 pub fn eval_condition
<F
>(cfg
: &ast
::MetaItem
, sess
: &ParseSess
, eval
: &mut F
)
521 where F
: FnMut(&ast
::MetaItem
) -> bool
524 ast
::MetaItemKind
::List(ref mis
) => {
525 for mi
in mis
.iter() {
526 if !mi
.is_meta_item() {
530 AttrError
::UnsupportedLiteral(
531 "unsupported literal",
539 // The unwraps below may look dangerous, but we've already asserted
540 // that they won't fail with the loop above.
541 match &*cfg
.name().as_str() {
542 "any" => mis
.iter().any(|mi
| {
543 eval_condition(mi
.meta_item().unwrap(), sess
, eval
)
545 "all" => mis
.iter().all(|mi
| {
546 eval_condition(mi
.meta_item().unwrap(), sess
, eval
)
550 span_err
!(sess
.span_diagnostic
, cfg
.span
, E0536
, "expected 1 cfg-pattern");
554 !eval_condition(mis
[0].meta_item().unwrap(), sess
, eval
)
557 span_err
!(sess
.span_diagnostic
, cfg
.span
, E0537
, "invalid predicate `{}`", p
);
562 ast
::MetaItemKind
::Word
| ast
::MetaItemKind
::NameValue(..) => {
569 #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Eq, Hash)]
570 pub struct Deprecation
{
571 pub since
: Option
<Symbol
>,
572 pub note
: Option
<Symbol
>,
575 /// Find the deprecation attribute. `None` if none exists.
576 pub fn find_deprecation(sess
: &ParseSess
, attrs
: &[Attribute
],
577 item_sp
: Span
) -> Option
<Deprecation
> {
578 find_deprecation_generic(sess
, attrs
.iter(), item_sp
)
581 fn find_deprecation_generic
<'a
, I
>(sess
: &ParseSess
,
584 -> Option
<Deprecation
>
585 where I
: Iterator
<Item
= &'a Attribute
>
587 let mut depr
: Option
<Deprecation
> = None
;
588 let diagnostic
= &sess
.span_diagnostic
;
590 'outer
: for attr
in attrs_iter
{
591 if attr
.path
!= "deprecated" {
598 span_err
!(diagnostic
, item_sp
, E0550
, "multiple deprecated attributes");
602 depr
= if let Some(metas
) = attr
.meta_item_list() {
603 let get
= |meta
: &MetaItem
, item
: &mut Option
<Symbol
>| {
605 handle_errors(sess
, meta
.span
, AttrError
::MultipleItem(meta
.name()));
608 if let Some(v
) = meta
.value_str() {
612 if let Some(lit
) = meta
.name_value_literal() {
616 AttrError
::UnsupportedLiteral(
617 "literal in `deprecated` \
618 value must be a string",
619 lit
.node
.is_bytestr()
623 span_err
!(diagnostic
, meta
.span
, E0551
, "incorrect meta item");
630 let mut since
= None
;
634 NestedMetaItemKind
::MetaItem(mi
) => {
635 match &*mi
.name().as_str() {
636 "since" => if !get(mi
, &mut since
) { continue 'outer }
,
637 "note" => if !get(mi
, &mut note
) { continue 'outer }
,
642 AttrError
::UnknownMetaItem(mi
.name(), &["since", "note"]),
648 NestedMetaItemKind
::Literal(lit
) => {
652 AttrError
::UnsupportedLiteral(
653 "item in `deprecated` must be a key/value pair",
662 Some(Deprecation {since: since, note: note}
)
664 Some(Deprecation{since: None, note: None}
)
671 #[derive(PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone)]
681 #[derive(Eq, Hash, PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone)]
683 SignedInt(ast
::IntTy
),
684 UnsignedInt(ast
::UintTy
)
689 pub fn is_signed(self) -> bool
{
690 use self::IntType
::*;
693 SignedInt(..) => true,
694 UnsignedInt(..) => false
699 /// Parse #[repr(...)] forms.
701 /// Valid repr contents: any of the primitive integral type names (see
702 /// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use
703 /// the same discriminant size that the corresponding C enum would or C
704 /// structure layout, `packed` to remove padding, and `transparent` to elegate representation
705 /// concerns to the only non-ZST field.
706 pub fn find_repr_attrs(sess
: &ParseSess
, attr
: &Attribute
) -> Vec
<ReprAttr
> {
707 use self::ReprAttr
::*;
709 let mut acc
= Vec
::new();
710 let diagnostic
= &sess
.span_diagnostic
;
711 if attr
.path
== "repr" {
712 if let Some(items
) = attr
.meta_item_list() {
715 if !item
.is_meta_item() {
719 AttrError
::UnsupportedLiteral(
720 "meta item in `repr` must be an identifier",
727 let mut recognised
= false;
728 if let Some(mi
) = item
.word() {
729 let word
= &*mi
.name().as_str();
730 let hint
= match word
{
732 "packed" => Some(ReprPacked(1)),
733 "simd" => Some(ReprSimd
),
734 "transparent" => Some(ReprTransparent
),
735 _
=> match int_type_of_word(word
) {
736 Some(ity
) => Some(ReprInt(ity
)),
743 if let Some(h
) = hint
{
747 } else if let Some((name
, value
)) = item
.name_value_literal() {
748 let parse_alignment
= |node
: &ast
::LitKind
| -> Result
<u32, &'
static str> {
749 if let ast
::LitKind
::Int(literal
, ast
::LitIntType
::Unsuffixed
) = node
{
750 if literal
.is_power_of_two() {
751 // rustc::ty::layout::Align restricts align to <= 2^29
752 if *literal
<= 1 << 29 {
755 Err("larger than 2^29")
758 Err("not a power of two")
761 Err("not an unsuffixed integer")
765 let mut literal_error
= None
;
768 match parse_alignment(&value
.node
) {
769 Ok(literal
) => acc
.push(ReprAlign(literal
)),
770 Err(message
) => literal_error
= Some(message
)
773 else if name
== "packed" {
775 match parse_alignment(&value
.node
) {
776 Ok(literal
) => acc
.push(ReprPacked(literal
)),
777 Err(message
) => literal_error
= Some(message
)
780 if let Some(literal_error
) = literal_error
{
781 span_err
!(diagnostic
, item
.span
, E0589
,
782 "invalid `repr(align)` attribute: {}", literal_error
);
785 if let Some(meta_item
) = item
.meta_item() {
786 if meta_item
.name() == "align" {
787 if let MetaItemKind
::NameValue(ref value
) = meta_item
.node
{
789 let mut err
= struct_span_err
!(diagnostic
, item
.span
, E0693
,
790 "incorrect `repr(align)` attribute format");
792 ast
::LitKind
::Int(int
, ast
::LitIntType
::Unsuffixed
) => {
793 err
.span_suggestion_with_applicability(
795 "use parentheses instead",
796 format
!("align({})", int
),
797 Applicability
::MachineApplicable
800 ast
::LitKind
::Str(s
, _
) => {
801 err
.span_suggestion_with_applicability(
803 "use parentheses instead",
804 format
!("align({})", s
),
805 Applicability
::MachineApplicable
816 // Not a word we recognize
817 span_err
!(diagnostic
, item
.span
, E0552
,
818 "unrecognized representation hint");
826 fn int_type_of_word(s
: &str) -> Option
<IntType
> {
827 use self::IntType
::*;
830 "i8" => Some(SignedInt(ast
::IntTy
::I8
)),
831 "u8" => Some(UnsignedInt(ast
::UintTy
::U8
)),
832 "i16" => Some(SignedInt(ast
::IntTy
::I16
)),
833 "u16" => Some(UnsignedInt(ast
::UintTy
::U16
)),
834 "i32" => Some(SignedInt(ast
::IntTy
::I32
)),
835 "u32" => Some(UnsignedInt(ast
::UintTy
::U32
)),
836 "i64" => Some(SignedInt(ast
::IntTy
::I64
)),
837 "u64" => Some(UnsignedInt(ast
::UintTy
::U64
)),
838 "i128" => Some(SignedInt(ast
::IntTy
::I128
)),
839 "u128" => Some(UnsignedInt(ast
::UintTy
::U128
)),
840 "isize" => Some(SignedInt(ast
::IntTy
::Isize
)),
841 "usize" => Some(UnsignedInt(ast
::UintTy
::Usize
)),