about summary refs log tree commit diff
path: root/compiler/rustc_attr_parsing/src/lib.rs
blob: 9841166b37dbdef3820f9ead4b03509c69d05327 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//! Centralized logic for parsing and attributes.
//!
//! Part of a series of crates:
//! - rustc_attr_data_structures: contains types that the parsers parse into
//! - rustc_attr_parsing: this crate
//! - (in the future): rustc_attr_validation
//!
//! History: Check out [#131229](https://github.com/rust-lang/rust/issues/131229).
//! There used to be only one definition of attributes in the compiler: `ast::Attribute`.
//! These were then parsed or validated or both in places distributed all over the compiler.
//! This was a mess...
//!
//! Attributes are markers on items.
//! Many of them are actually attribute-like proc-macros, and are expanded to some other rust syntax.
//! This could either be a user provided proc macro, or something compiler provided.
//! `derive` is an example of one that the compiler provides.
//! These are built-in, but they have a valid expansion to Rust tokens and are thus called "active".
//! I personally like calling these *active* compiler-provided attributes, built-in *macros*,
//! because they still expand, and this helps to differentiate them from built-in *attributes*.
//! However, I'll be the first to admit that the naming here can be confusing.
//!
//! The alternative to active attributes, are inert attributes.
//! These can occur in user code (proc-macro helper attributes).
//! But what's important is, many built-in attributes are inert like this.
//! There is nothing they expand to during the macro expansion process,
//! sometimes because they literally cannot expand to something that is valid Rust.
//! They are really just markers to guide the compilation process.
//! An example is `#[inline(...)]` which changes how code for functions is generated.
//!
//! ```text
//!                      Active                 Inert
//!              ┌──────────────────────┬──────────────────────┐
//!              │     (mostly in)      │    these are parsed  │
//!              │ rustc_builtin_macros │        here!         │
//!              │                      │                      │
//!              │                      │                      │
//!              │    #[derive(...)]    │    #[stable()]       │
//!     Built-in │    #[cfg()]          │    #[inline()]       │
//!              │    #[cfg_attr()]     │    #[repr()]         │
//!              │                      │                      │
//!              │                      │                      │
//!              │                      │                      │
//!              ├──────────────────────┼──────────────────────┤
//!              │                      │                      │
//!              │                      │                      │
//!              │                      │       `b` in         │
//!              │                      │ #[proc_macro_derive( │
//! User created │ #[proc_macro_attr()] │    a,                │
//!              │                      │    attributes(b)     │
//!              │                      │ ]                    │
//!              │                      │                      │
//!              │                      │                      │
//!              │                      │                      │
//!              └──────────────────────┴──────────────────────┘
//! ```
//!
//! In this crate, syntactical attributes (sequences of tokens that look like
//! `#[something(something else)]`) are parsed into more semantic attributes, markers on items.
//! Multiple syntactic attributes might influence a single semantic attribute. For example,
//! `#[stable(...)]` and `#[unstable()]` cannot occur together, and both semantically define
//! a "stability" of an item. So, the stability attribute has an
//! [`AttributeParser`](attributes::AttributeParser) that recognizes both the `#[stable()]`
//! and `#[unstable()]` syntactic attributes, and at the end produce a single [`AttributeKind::Stability`].
//!
//! As a rule of thumb, when a syntactical attribute can be applied more than once, they should be
//! combined into a single semantic attribute. For example:
//!
//! ```
//! #[repr(C)]
//! #[repr(packed)]
//! struct Meow {}
//! ```
//!
//! should result in a single `AttributeKind::Repr` containing a list of repr annotations, in this
//! case `C` and `packed`. This is equivalent to writing `#[repr(C, packed)]` in a single
//! syntactical annotation.

// tidy-alphabetical-start
#![allow(internal_features)]
#![doc(rust_logo)]
#![feature(let_chains)]
#![feature(rustdoc_internals)]
#![warn(unreachable_pub)]
// tidy-alphabetical-end

#[macro_use]
mod attributes;
mod context;
pub mod parser;
mod session_diagnostics;

pub use attributes::cfg::*;
pub use attributes::util::{find_crate_name, is_builtin_attr, parse_version};
pub use context::{AttributeParser, OmitDoc};
pub use rustc_attr_data_structures::*;

rustc_fluent_macro::fluent_messages! { "../messages.ftl" }

/// Finds attributes in sequences of attributes by pattern matching.
///
/// A little like `matches` but for attributes.
///
/// ```rust,ignore (illustrative)
/// // finds the repr attribute
/// if let Some(r) = find_attr!(attrs, AttributeKind::Repr(r) => r) {
///
/// }
///
/// // checks if one has matched
/// if find_attr!(attrs, AttributeKind::Repr(_)) {
///
/// }
/// ```
///
/// Often this requires you to first end up with a list of attributes.
/// A common way to get those is through `tcx.get_all_attrs(did)`
#[macro_export]
macro_rules! find_attr {
    ($attributes_list: expr, $pattern: pat $(if $guard: expr)?) => {{
        $crate::find_attr!($attributes_list, $pattern $(if $guard)? => ()).is_some()
    }};

    ($attributes_list: expr, $pattern: pat $(if $guard: expr)? => $e: expr) => {{
        fn check_attribute_iterator<'a>(_: &'_ impl IntoIterator<Item = &'a rustc_hir::Attribute>) {}
        check_attribute_iterator(&$attributes_list);

        let find_attribute = |iter| {
            for i in $attributes_list {
                match i {
                    rustc_hir::Attribute::Parsed($pattern) $(if $guard)? => {
                        return Some($e);
                    }
                    _ => {}
                }
            }

            None
        };
        find_attribute($attributes_list)
    }};
}