Skip to content

Commit d76dba2

Browse files
committed
Implement token-based attribute handling with acceptable performance
1 parent 8e863eb commit d76dba2

33 files changed

+2720
-702
lines changed

compiler/rustc_ast/src/ast.rs

+32-20
Original file line numberDiff line numberDiff line change
@@ -905,26 +905,6 @@ pub struct Stmt {
905905
}
906906

907907
impl Stmt {
908-
pub fn tokens(&self) -> Option<&LazyTokenStream> {
909-
match self.kind {
910-
StmtKind::Local(ref local) => local.tokens.as_ref(),
911-
StmtKind::Item(ref item) => item.tokens.as_ref(),
912-
StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => expr.tokens.as_ref(),
913-
StmtKind::Empty => None,
914-
StmtKind::MacCall(ref mac) => mac.tokens.as_ref(),
915-
}
916-
}
917-
918-
pub fn tokens_mut(&mut self) -> Option<&mut LazyTokenStream> {
919-
match self.kind {
920-
StmtKind::Local(ref mut local) => local.tokens.as_mut(),
921-
StmtKind::Item(ref mut item) => item.tokens.as_mut(),
922-
StmtKind::Expr(ref mut expr) | StmtKind::Semi(ref mut expr) => expr.tokens.as_mut(),
923-
StmtKind::Empty => None,
924-
StmtKind::MacCall(ref mut mac) => mac.tokens.as_mut(),
925-
}
926-
}
927-
928908
pub fn has_trailing_semicolon(&self) -> bool {
929909
match &self.kind {
930910
StmtKind::Semi(_) => true,
@@ -979,6 +959,38 @@ pub enum StmtKind {
979959
MacCall(P<MacCallStmt>),
980960
}
981961

962+
impl StmtKind {
963+
pub fn tokens(&self) -> Option<&LazyTokenStream> {
964+
match self {
965+
StmtKind::Local(ref local) => local.tokens.as_ref(),
966+
StmtKind::Item(ref item) => item.tokens.as_ref(),
967+
StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => expr.tokens.as_ref(),
968+
StmtKind::Empty => None,
969+
StmtKind::MacCall(ref mac) => mac.tokens.as_ref(),
970+
}
971+
}
972+
973+
pub fn tokens_mut(&mut self) -> Option<&mut LazyTokenStream> {
974+
match self {
975+
StmtKind::Local(ref mut local) => local.tokens.as_mut(),
976+
StmtKind::Item(ref mut item) => item.tokens.as_mut(),
977+
StmtKind::Expr(ref mut expr) | StmtKind::Semi(ref mut expr) => expr.tokens.as_mut(),
978+
StmtKind::Empty => None,
979+
StmtKind::MacCall(ref mut mac) => mac.tokens.as_mut(),
980+
}
981+
}
982+
983+
pub fn set_tokens(&mut self, tokens: Option<LazyTokenStream>) {
984+
match self {
985+
StmtKind::Local(ref mut local) => local.tokens = tokens,
986+
StmtKind::Item(ref mut item) => item.tokens = tokens,
987+
StmtKind::Expr(ref mut expr) | StmtKind::Semi(ref mut expr) => expr.tokens = tokens,
988+
StmtKind::Empty => {}
989+
StmtKind::MacCall(ref mut mac) => mac.tokens = tokens,
990+
}
991+
}
992+
}
993+
982994
#[derive(Clone, Encodable, Decodable, Debug)]
983995
pub struct MacCallStmt {
984996
pub mac: MacCall,

compiler/rustc_ast/src/ast_like.rs

+125-26
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,42 @@
11
use super::ptr::P;
2-
use super::tokenstream::LazyTokenStream;
2+
use super::tokenstream::{LazyTokenStream, Spacing, AttributesData, PreexpTokenTree, PreexpTokenStream};
33
use super::{Arm, Field, FieldPat, GenericParam, Param, StructField, Variant};
4-
use super::{AssocItem, Expr, ForeignItem, Item, Local};
4+
use super::{AssocItem, Expr, ForeignItem, Item, Local, MacCallStmt};
55
use super::{AttrItem, AttrKind, Block, Pat, Path, Ty, Visibility};
66
use super::{AttrVec, Attribute, Stmt, StmtKind};
7+
use rustc_span::sym;
78

89
/// An `AstLike` represents an AST node (or some wrapper around
910
/// and AST node) which stores some combination of attributes
1011
/// and tokens.
1112
pub trait AstLike: Sized {
13+
const SUPPORTS_INNER_ATTRS: bool;
1214
fn attrs(&self) -> &[Attribute];
1315
fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>));
1416
/// Called by `Parser::collect_tokens` to store the collected
1517
/// tokens inside an AST node
16-
fn finalize_tokens(&mut self, _tokens: LazyTokenStream) {
18+
fn finalize_tokens(&mut self, _tokens: LazyTokenStream) -> Option<AttributesData> {
1719
// This default impl makes this trait easier to implement
1820
// in tools like `rust-analyzer`
1921
panic!("`finalize_tokens` is not supported!")
2022
}
23+
fn visit_tokens(&mut self, f: impl FnOnce(&mut Option<LazyTokenStream>));
2124
}
2225

2326
impl<T: AstLike + 'static> AstLike for P<T> {
27+
const SUPPORTS_INNER_ATTRS: bool = T::SUPPORTS_INNER_ATTRS;
2428
fn attrs(&self) -> &[Attribute] {
2529
(**self).attrs()
2630
}
2731
fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
2832
(**self).visit_attrs(f);
2933
}
30-
fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
34+
fn finalize_tokens(&mut self, tokens: LazyTokenStream) -> Option<AttributesData> {
3135
(**self).finalize_tokens(tokens)
3236
}
37+
fn visit_tokens(&mut self, f: impl FnOnce(&mut Option<LazyTokenStream>)) {
38+
(**self).visit_tokens(f);
39+
}
3340
}
3441

3542
fn visit_attrvec(attrs: &mut AttrVec, f: impl FnOnce(&mut Vec<Attribute>)) {
@@ -41,6 +48,10 @@ fn visit_attrvec(attrs: &mut AttrVec, f: impl FnOnce(&mut Vec<Attribute>)) {
4148
}
4249

4350
impl AstLike for StmtKind {
51+
// This might be an `StmtKind::Item`, which contains
52+
// an item that supports inner attrs
53+
const SUPPORTS_INNER_ATTRS: bool = true;
54+
4455
fn attrs(&self) -> &[Attribute] {
4556
match *self {
4657
StmtKind::Local(ref local) => local.attrs(),
@@ -60,53 +71,85 @@ impl AstLike for StmtKind {
6071
StmtKind::MacCall(mac) => visit_attrvec(&mut mac.attrs, f),
6172
}
6273
}
63-
fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
64-
let stmt_tokens = match self {
65-
StmtKind::Local(ref mut local) => &mut local.tokens,
66-
StmtKind::Item(ref mut item) => &mut item.tokens,
67-
StmtKind::Expr(ref mut expr) | StmtKind::Semi(ref mut expr) => &mut expr.tokens,
68-
StmtKind::Empty => return,
69-
StmtKind::MacCall(ref mut mac) => &mut mac.tokens,
74+
fn finalize_tokens(&mut self, tokens: LazyTokenStream) -> Option<AttributesData> {
75+
match self {
76+
StmtKind::Local(ref mut local) => local.finalize_tokens(tokens),
77+
StmtKind::MacCall(ref mut mac) => mac.finalize_tokens(tokens),
78+
StmtKind::Expr(ref mut expr) | StmtKind::Semi(ref mut expr) => {
79+
expr.finalize_tokens(tokens)
80+
}
81+
StmtKind::Item(ref mut item) => item.finalize_tokens(tokens),
82+
StmtKind::Empty => None,
83+
}
84+
}
85+
fn visit_tokens(&mut self, f: impl FnOnce(&mut Option<LazyTokenStream>)) {
86+
let tokens = match self {
87+
StmtKind::Local(ref mut local) => Some(&mut local.tokens),
88+
StmtKind::Item(ref mut item) => Some(&mut item.tokens),
89+
StmtKind::Expr(ref mut expr) | StmtKind::Semi(ref mut expr) => Some(&mut expr.tokens),
90+
StmtKind::Empty => None,
91+
StmtKind::MacCall(ref mut mac) => Some(&mut mac.tokens),
7092
};
71-
if stmt_tokens.is_none() {
72-
*stmt_tokens = Some(tokens);
93+
if let Some(tokens) = tokens {
94+
f(tokens);
7395
}
7496
}
7597
}
7698

7799
impl AstLike for Stmt {
100+
const SUPPORTS_INNER_ATTRS: bool = StmtKind::SUPPORTS_INNER_ATTRS;
101+
78102
fn attrs(&self) -> &[Attribute] {
79103
self.kind.attrs()
80104
}
81105

82106
fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
83107
self.kind.visit_attrs(f);
84108
}
85-
fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
109+
fn finalize_tokens(&mut self, tokens: LazyTokenStream) -> Option<AttributesData> {
86110
self.kind.finalize_tokens(tokens)
87111
}
112+
fn visit_tokens(&mut self, f: impl FnOnce(&mut Option<LazyTokenStream>)) {
113+
self.kind.visit_tokens(f)
114+
}
88115
}
89116

90117
impl AstLike for Attribute {
118+
const SUPPORTS_INNER_ATTRS: bool = false;
119+
91120
fn attrs(&self) -> &[Attribute] {
92121
&[]
93122
}
94123
fn visit_attrs(&mut self, _f: impl FnOnce(&mut Vec<Attribute>)) {}
95-
fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
124+
fn finalize_tokens(&mut self, tokens: LazyTokenStream) -> Option<AttributesData> {
96125
match &mut self.kind {
97126
AttrKind::Normal(_, attr_tokens) => {
98127
if attr_tokens.is_none() {
99128
*attr_tokens = Some(tokens);
100129
}
130+
None
101131
}
102132
AttrKind::DocComment(..) => {
103133
panic!("Called finalize_tokens on doc comment attr {:?}", self)
104134
}
105135
}
106136
}
137+
fn visit_tokens(&mut self, f: impl FnOnce(&mut Option<LazyTokenStream>)) {
138+
match &mut self.kind {
139+
AttrKind::Normal(_, attr_tokens) => {
140+
f(attr_tokens);
141+
}
142+
AttrKind::DocComment(..) => {
143+
panic!("Called visit_tokens on doc comment attr {:?}", self)
144+
}
145+
}
146+
147+
}
107148
}
108149

109150
impl<T: AstLike> AstLike for Option<T> {
151+
const SUPPORTS_INNER_ATTRS: bool = T::SUPPORTS_INNER_ATTRS;
152+
110153
fn attrs(&self) -> &[Attribute] {
111154
self.as_ref().map(|inner| inner.attrs()).unwrap_or(&[])
112155
}
@@ -115,13 +158,26 @@ impl<T: AstLike> AstLike for Option<T> {
115158
inner.visit_attrs(f);
116159
}
117160
}
118-
fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
161+
fn finalize_tokens(&mut self, tokens: LazyTokenStream) -> Option<AttributesData> {
162+
self.as_mut().and_then(|inner| inner.finalize_tokens(tokens))
163+
}
164+
fn visit_tokens(&mut self, f: impl FnOnce(&mut Option<LazyTokenStream>)) {
119165
if let Some(inner) = self {
120-
inner.finalize_tokens(tokens);
166+
inner.visit_tokens(f);
121167
}
122168
}
123169
}
124170

171+
// NOTE: Builtin attributes like `cfg` and `cfg_attr` cannot be renamed via imports.
172+
// Therefore, the absence of a literal `cfg` or `cfg_attr` guarantees that
173+
// we don't need to do any eager expansion.
174+
pub fn has_cfg_or_cfg_any(attrs: &[Attribute]) -> bool {
175+
attrs.iter().any(|attr| {
176+
attr.ident().map_or(false, |ident| ident.name == sym::cfg || ident.name == sym::cfg_attr)
177+
})
178+
}
179+
180+
125181
/// Helper trait for the macros below. Abstracts over
126182
/// the two types of attribute fields that AST nodes
127183
/// may have (`Vec<Attribute>` or `AttrVec`)
@@ -142,8 +198,13 @@ impl VecOrAttrVec for AttrVec {
142198
}
143199

144200
macro_rules! derive_has_tokens_and_attrs {
145-
($($ty:path),*) => { $(
201+
(
202+
const SUPPORTS_INNER_ATTRS: bool = $inner_attrs:literal;
203+
$($ty:path),*
204+
) => { $(
146205
impl AstLike for $ty {
206+
const SUPPORTS_INNER_ATTRS: bool = $inner_attrs;
207+
147208
fn attrs(&self) -> &[Attribute] {
148209
&self.attrs
149210
}
@@ -152,19 +213,31 @@ macro_rules! derive_has_tokens_and_attrs {
152213
VecOrAttrVec::visit(&mut self.attrs, f)
153214
}
154215

155-
fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
216+
fn finalize_tokens(&mut self, tokens: LazyTokenStream) -> Option<AttributesData> {
156217
if self.tokens.is_none() {
157218
self.tokens = Some(tokens);
158219
}
159220

221+
if has_cfg_or_cfg_any(&self.attrs) {
222+
Some(AttributesData { attrs: self.attrs.clone().into(), tokens: self.tokens.clone().unwrap() })
223+
} else {
224+
None
225+
}
226+
}
227+
228+
fn visit_tokens(&mut self, f: impl FnOnce(&mut Option<LazyTokenStream>)) {
229+
f(&mut self.tokens)
160230
}
231+
161232
}
162233
)* }
163234
}
164235

165236
macro_rules! derive_has_attrs_no_tokens {
166237
($($ty:path),*) => { $(
167238
impl AstLike for $ty {
239+
const SUPPORTS_INNER_ATTRS: bool = false;
240+
168241
fn attrs(&self) -> &[Attribute] {
169242
&self.attrs
170243
}
@@ -173,35 +246,61 @@ macro_rules! derive_has_attrs_no_tokens {
173246
VecOrAttrVec::visit(&mut self.attrs, f)
174247
}
175248

176-
fn finalize_tokens(&mut self, _tokens: LazyTokenStream) {}
249+
fn finalize_tokens(&mut self, tokens: LazyTokenStream) -> Option<AttributesData> {
250+
if has_cfg_or_cfg_any(&self.attrs) {
251+
Some(AttributesData { attrs: self.attrs.clone().into(), tokens })
252+
} else {
253+
None
254+
}
255+
}
256+
fn visit_tokens(&mut self, _f: impl FnOnce(&mut Option<LazyTokenStream>)) {}
257+
177258
}
178259
)* }
179260
}
180261

181262
macro_rules! derive_has_tokens_no_attrs {
182263
($($ty:path),*) => { $(
183264
impl AstLike for $ty {
265+
const SUPPORTS_INNER_ATTRS: bool = false;
266+
184267
fn attrs(&self) -> &[Attribute] {
185268
&[]
186269
}
187270

188271
fn visit_attrs(&mut self, _f: impl FnOnce(&mut Vec<Attribute>)) {
189272
}
190273

191-
fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
274+
fn finalize_tokens(&mut self, tokens: LazyTokenStream) -> Option<AttributesData> {
275+
// FIXME - stoer them directly?
192276
if self.tokens.is_none() {
193-
self.tokens = Some(tokens);
277+
self.tokens = Some(LazyTokenStream::new(PreexpTokenStream::new(vec![
278+
(PreexpTokenTree::Attributes(AttributesData {
279+
attrs: Default::default(),
280+
tokens,
281+
}), Spacing::Alone)])));
194282
}
195-
283+
None
284+
}
285+
fn visit_tokens(&mut self, f: impl FnOnce(&mut Option<LazyTokenStream>)) {
286+
f(&mut self.tokens)
196287
}
197288
}
198289
)* }
199290
}
200291

201-
// These AST nodes support both inert and active
202-
// attributes, so they also have tokens.
292+
// These ast nodes support both active and inert attributes,
293+
// so they have tokens collected to pass to proc macros
294+
derive_has_tokens_and_attrs! {
295+
// Both `Item` and `AssocItem` can have bodies, which
296+
// can contain inner attributes
297+
const SUPPORTS_INNER_ATTRS: bool = true;
298+
Item, AssocItem
299+
}
300+
203301
derive_has_tokens_and_attrs! {
204-
Item, Expr, Local, AssocItem, ForeignItem
302+
const SUPPORTS_INNER_ATTRS: bool = false;
303+
ForeignItem, Expr, Local, MacCallStmt
205304
}
206305

207306
// These ast nodes only support inert attributes, so they don't

0 commit comments

Comments
 (0)