Skip to content

Commit d6ebbbf

Browse files
committed
Factor out AttrsTarget flattening code.
This commit does the following. - Pulls the code out of `AttrTokenStream::to_token_trees` into a new function `attrs_and_tokens_to_token_trees`. - Simplifies `TokenStream::from_ast` by calling the new function. This is nicer than the old way, which created a temporary `AttrTokenStream` containing a single `AttrsTarget` (which required some cloning) just to call `to_token_trees` on it. (It is good to remove this use of `AttrsTarget` which isn't related to `cfg_attr` expansion.)
1 parent d8b6aa6 commit d6ebbbf

File tree

2 files changed

+72
-65
lines changed

2 files changed

+72
-65
lines changed

compiler/rustc_ast/src/tokenstream.rs

+68-64
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
use crate::ast::{AttrStyle, StmtKind};
1717
use crate::ast_traits::{HasAttrs, HasTokens};
1818
use crate::token::{self, Delimiter, Nonterminal, Token, TokenKind};
19-
use crate::AttrVec;
19+
use crate::{AttrVec, Attribute};
2020

2121
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
2222
use rustc_data_structures::sync::{self, Lrc};
@@ -179,11 +179,10 @@ impl AttrTokenStream {
179179
AttrTokenStream(Lrc::new(tokens))
180180
}
181181

182-
/// Converts this `AttrTokenStream` to a plain `Vec<TokenTree>`.
183-
/// During conversion, `AttrTokenTree::AttrsTarget` get 'flattened'
184-
/// back to a `TokenStream` of the form `outer_attr attr_target`.
185-
/// If there are inner attributes, they are inserted into the proper
186-
/// place in the attribute target tokens.
182+
/// Converts this `AttrTokenStream` to a plain `Vec<TokenTree>`. During
183+
/// conversion, any `AttrTokenTree::AttrsTarget` gets "flattened" back to a
184+
/// `TokenStream`, as described in the comment on
185+
/// `attrs_and_tokens_to_token_trees`.
187186
pub fn to_token_trees(&self) -> Vec<TokenTree> {
188187
let mut res = Vec::with_capacity(self.0.len());
189188
for tree in self.0.iter() {
@@ -200,59 +199,72 @@ impl AttrTokenStream {
200199
))
201200
}
202201
AttrTokenTree::AttrsTarget(target) => {
203-
let idx = target
204-
.attrs
205-
.partition_point(|attr| matches!(attr.style, crate::AttrStyle::Outer));
206-
let (outer_attrs, inner_attrs) = target.attrs.split_at(idx);
207-
208-
let mut target_tokens = target.tokens.to_attr_token_stream().to_token_trees();
209-
if !inner_attrs.is_empty() {
210-
let mut found = false;
211-
// Check the last two trees (to account for a trailing semi)
212-
for tree in target_tokens.iter_mut().rev().take(2) {
213-
if let TokenTree::Delimited(span, spacing, delim, delim_tokens) = tree {
214-
// Inner attributes are only supported on extern blocks, functions,
215-
// impls, and modules. All of these have their inner attributes
216-
// placed at the beginning of the rightmost outermost braced group:
217-
// e.g. fn foo() { #![my_attr] }
218-
//
219-
// Therefore, we can insert them back into the right location
220-
// without needing to do any extra position tracking.
221-
//
222-
// Note: Outline modules are an exception - they can
223-
// have attributes like `#![my_attr]` at the start of a file.
224-
// Support for custom attributes in this position is not
225-
// properly implemented - we always synthesize fake tokens,
226-
// so we never reach this code.
227-
228-
let mut tts = vec![];
229-
for inner_attr in inner_attrs {
230-
tts.extend(inner_attr.token_trees());
231-
}
232-
tts.extend(delim_tokens.0.iter().cloned());
233-
let stream = TokenStream::new(tts);
234-
*tree = TokenTree::Delimited(*span, *spacing, *delim, stream);
235-
found = true;
236-
break;
237-
}
238-
}
239-
240-
assert!(
241-
found,
242-
"Failed to find trailing delimited group in: {target_tokens:?}"
243-
);
244-
}
245-
for attr in outer_attrs {
246-
res.extend(attr.token_trees());
247-
}
248-
res.extend(target_tokens);
202+
attrs_and_tokens_to_token_trees(&target.attrs, &target.tokens, &mut res);
249203
}
250204
}
251205
}
252206
res
253207
}
254208
}
255209

210+
// Converts multiple attributes and the tokens for a target AST node into token trees, and appends
211+
// them to `res`.
212+
//
213+
// Example: if the AST node is "fn f() { blah(); }", then:
214+
// - Simple if no attributes are present, e.g. "fn f() { blah(); }"
215+
// - Simple if only outer attribute are present, e.g. "#[outer1] #[outer2] fn f() { blah(); }"
216+
// - Trickier if inner attributes are present, because they must be moved within the AST node's
217+
// tokens, e.g. "#[outer] fn f() { #![inner] blah() }"
218+
fn attrs_and_tokens_to_token_trees(
219+
attrs: &[Attribute],
220+
target_tokens: &LazyAttrTokenStream,
221+
res: &mut Vec<TokenTree>,
222+
) {
223+
let idx = attrs.partition_point(|attr| matches!(attr.style, crate::AttrStyle::Outer));
224+
let (outer_attrs, inner_attrs) = attrs.split_at(idx);
225+
226+
// Add outer attribute tokens.
227+
for attr in outer_attrs {
228+
res.extend(attr.token_trees());
229+
}
230+
231+
// Add target AST node tokens.
232+
res.extend(target_tokens.to_attr_token_stream().to_token_trees());
233+
234+
// Insert inner attribute tokens.
235+
if !inner_attrs.is_empty() {
236+
let mut found = false;
237+
// Check the last two trees (to account for a trailing semi)
238+
for tree in res.iter_mut().rev().take(2) {
239+
if let TokenTree::Delimited(span, spacing, delim, delim_tokens) = tree {
240+
// Inner attributes are only supported on extern blocks, functions,
241+
// impls, and modules. All of these have their inner attributes
242+
// placed at the beginning of the rightmost outermost braced group:
243+
// e.g. fn foo() { #![my_attr] }
244+
//
245+
// Therefore, we can insert them back into the right location
246+
// without needing to do any extra position tracking.
247+
//
248+
// Note: Outline modules are an exception - they can
249+
// have attributes like `#![my_attr]` at the start of a file.
250+
// Support for custom attributes in this position is not
251+
// properly implemented - we always synthesize fake tokens,
252+
// so we never reach this code.
253+
let mut tts = vec![];
254+
for inner_attr in inner_attrs {
255+
tts.extend(inner_attr.token_trees());
256+
}
257+
tts.extend(delim_tokens.0.iter().cloned());
258+
let stream = TokenStream::new(tts);
259+
*tree = TokenTree::Delimited(*span, *spacing, *delim, stream);
260+
found = true;
261+
break;
262+
}
263+
}
264+
assert!(found, "Failed to find trailing delimited group in: {res:?}");
265+
}
266+
}
267+
256268
/// Stores the tokens for an attribute target, along
257269
/// with its attributes.
258270
///
@@ -438,18 +450,10 @@ impl TokenStream {
438450
}
439451

440452
pub fn from_ast(node: &(impl HasAttrs + HasTokens + fmt::Debug)) -> TokenStream {
441-
let Some(tokens) = node.tokens() else {
442-
panic!("missing tokens for node: {:?}", node);
443-
};
444-
let attrs = node.attrs();
445-
let attr_stream = if attrs.is_empty() {
446-
tokens.to_attr_token_stream()
447-
} else {
448-
let target =
449-
AttrsTarget { attrs: attrs.iter().cloned().collect(), tokens: tokens.clone() };
450-
AttrTokenStream::new(vec![AttrTokenTree::AttrsTarget(target)])
451-
};
452-
TokenStream::new(attr_stream.to_token_trees())
453+
let tokens = node.tokens().unwrap_or_else(|| panic!("missing tokens for node: {:?}", node));
454+
let mut tts = vec![];
455+
attrs_and_tokens_to_token_trees(node.attrs(), tokens, &mut tts);
456+
TokenStream::new(tts)
453457
}
454458

455459
pub fn from_nonterminal_ast(nt: &Nonterminal) -> TokenStream {

compiler/rustc_expand/src/config.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,10 @@ impl<'a> StripUnconfigured<'a> {
283283
.flat_map(|item| self.process_cfg_attr(&self.expand_cfg_attr_item(cfg_attr, item)))
284284
.collect()
285285
} else {
286-
expanded_attrs.into_iter().map(|item| self.expand_cfg_attr_item(cfg_attr, item)).collect()
286+
expanded_attrs
287+
.into_iter()
288+
.map(|item| self.expand_cfg_attr_item(cfg_attr, item))
289+
.collect()
287290
}
288291
}
289292

0 commit comments

Comments
 (0)