1use std::{cell::OnceCell, ops::ControlFlow};
3
4use ::tt::TextRange;
5use base_db::{Crate, SourceDatabase};
6use cfg::CfgExpr;
7use parser::T;
8use smallvec::SmallVec;
9use syntax::{
10 AstNode, PreorderWithTokens, SyntaxElement, SyntaxNode, SyntaxToken, WalkEvent,
11 ast::{self, HasAttrs},
12};
13use syntax_bridge::DocCommentDesugarMode;
14
15use crate::{
16 attrs::{AstPathExt, AttrId, expand_cfg_attr, is_item_tree_filtered_attr},
17 fixup::{self, SyntaxFixupUndoInfo},
18 span_map::SpanMap,
19 tt::{self, DelimSpan, Span},
20};
21
22struct ItemIsCfgedOut;
23
24#[derive(Debug)]
25struct ExpandedAttrToProcess {
26 attr: ast::Meta,
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30enum NextExpandedAttrState {
31 NotStarted,
32 InTheMiddle,
33}
34
35#[derive(Debug)]
36struct AstAttrToProcess {
37 range: TextRange,
38 expanded_attrs: SmallVec<[ExpandedAttrToProcess; 1]>,
39 expanded_attrs_idx: usize,
40 next_expanded_attr: NextExpandedAttrState,
41 pound_span: Span,
42 brackets_span: DelimSpan,
43 excl_span: Option<Span>,
45}
46
47fn macro_input_callback(
48 db: &dyn SourceDatabase,
49 is_derive: bool,
50 censor_item_tree_attr_ids: &[AttrId],
51 krate: Crate,
52 default_span: Span,
53 span_map: SpanMap<'_>,
54) -> impl FnMut(&mut PreorderWithTokens, &WalkEvent<SyntaxElement>) -> (bool, Vec<tt::Leaf>) {
55 let cfg_options = OnceCell::new();
56 let cfg_options = move || *cfg_options.get_or_init(|| krate.cfg_options(db));
57
58 let mut should_strip_attr = {
59 let mut item_tree_attr_id = 0;
60 let mut censor_item_tree_attr_ids_index = 0;
61 move || {
62 let mut result = false;
63 if let Some(&next_censor_attr_id) =
64 censor_item_tree_attr_ids.get(censor_item_tree_attr_ids_index)
65 && next_censor_attr_id.item_tree_index() == item_tree_attr_id
66 {
67 censor_item_tree_attr_ids_index += 1;
68 result = true;
69 }
70 item_tree_attr_id += 1;
71 result
72 }
73 };
74
75 let mut attrs = Vec::new();
76 let mut attrs_idx = 0;
77 let mut has_inner_attrs_owner = false;
78 let mut in_attr = false;
79 let mut done_with_attrs = false;
80 let mut did_top_attrs = false;
81 move |preorder, event| {
82 match event {
83 WalkEvent::Enter(SyntaxElement::Node(node)) => {
84 if done_with_attrs {
85 return (true, Vec::new());
86 }
87
88 if ast::Attr::can_cast(node.kind()) {
89 in_attr = true;
90 let node_range = node.text_range();
91 while attrs
92 .get(attrs_idx)
93 .is_some_and(|it: &AstAttrToProcess| it.range != node_range)
94 {
95 attrs_idx += 1;
96 }
97 } else if !in_attr && let Some(has_attrs) = ast::AnyHasAttrs::cast(node.clone()) {
98 if has_inner_attrs_owner {
102 has_inner_attrs_owner = false;
103 return (true, Vec::new());
104 }
105
106 if did_top_attrs && !is_derive {
107 done_with_attrs = true;
109 return (true, Vec::new());
110 }
111 did_top_attrs = true;
112
113 if let Some(inner_attrs_node) = has_attrs.inner_attributes_node()
114 && inner_attrs_node != *node
115 {
116 has_inner_attrs_owner = true;
117 }
118
119 let node_attrs = ast::attrs_including_inner(&has_attrs);
120
121 attrs.clear();
122 node_attrs.clone().for_each(|attr| {
123 let span_for = |token: Option<SyntaxToken>| {
124 token
125 .map(|token| span_map.span_for_range(token.text_range()))
126 .unwrap_or(default_span)
127 };
128 attrs.push(AstAttrToProcess {
129 range: attr.syntax().text_range(),
130 pound_span: span_for(attr.pound_token()),
131 brackets_span: DelimSpan {
132 open: span_for(attr.l_brack_token()),
133 close: span_for(attr.r_brack_token()),
134 },
135 excl_span: attr
136 .excl_token()
137 .map(|token| span_map.span_for_range(token.text_range())),
138 expanded_attrs: SmallVec::new(),
139 expanded_attrs_idx: 0,
140 next_expanded_attr: NextExpandedAttrState::NotStarted,
141 });
142 });
143
144 attrs_idx = 0;
145 let strip_current_item =
146 expand_cfg_attr(node_attrs, &cfg_options, |attr, top_attr| {
147 while attrs[attrs_idx].range != top_attr.syntax().text_range() {
149 attrs_idx += 1;
150 }
151
152 let mut strip_current_attr = false;
153 match &attr {
154 ast::Meta::CfgMeta(attr) => {
155 if let Some(cfg_predicate) = attr.cfg_predicate() {
156 let cfg_expr = CfgExpr::parse_from_ast(cfg_predicate);
157 if cfg_options().check(&cfg_expr) == Some(false) {
158 return ControlFlow::Break(ItemIsCfgedOut);
159 }
160 strip_current_attr = true;
161 }
162 }
163 _ => {
164 if attr
165 .path()
166 .as_one_segment()
167 .is_none_or(|name| !is_item_tree_filtered_attr(&name))
168 {
169 strip_current_attr = should_strip_attr();
170 }
171 }
172 }
173
174 if !strip_current_attr {
175 attrs[attrs_idx]
176 .expanded_attrs
177 .push(ExpandedAttrToProcess { attr });
178 }
179
180 ControlFlow::Continue(())
181 });
182 attrs_idx = 0;
183
184 if strip_current_item.is_some() {
185 preorder.skip_subtree();
186 attrs.clear();
187
188 'eat_comma: {
189 let mut events_until_comma = 0;
191 for event in preorder.clone() {
192 match event {
193 WalkEvent::Enter(SyntaxElement::Node(_))
194 | WalkEvent::Leave(_) => {}
195 WalkEvent::Enter(SyntaxElement::Token(token)) => {
196 let kind = token.kind();
197 if kind == T![,] {
198 break;
199 } else if !kind.is_trivia() {
200 break 'eat_comma;
201 }
202 }
203 }
204 events_until_comma += 1;
205 }
206 preorder.nth(events_until_comma);
207 }
208
209 return (false, Vec::new());
210 }
211 }
212 }
213 WalkEvent::Leave(SyntaxElement::Node(node)) => {
214 if ast::Attr::can_cast(node.kind()) {
215 in_attr = false;
216 attrs_idx += 1;
217 }
218 }
219 WalkEvent::Enter(SyntaxElement::Token(token)) => {
220 if !in_attr {
221 return (true, Vec::new());
222 }
223
224 let Some(ast_attr) = attrs.get_mut(attrs_idx) else {
225 return (true, Vec::new());
226 };
227 let token_range = token.text_range();
228 let Some(expanded_attr) = ast_attr.expanded_attrs.get(ast_attr.expanded_attrs_idx)
229 else {
230 return (false, Vec::new());
233 };
234 match ast_attr.next_expanded_attr {
235 NextExpandedAttrState::NotStarted => {
236 if token_range.start() >= expanded_attr.attr.syntax().text_range().start() {
237 let mut insert_tokens = Vec::with_capacity(3);
239 insert_tokens.push(tt::Leaf::Punct(tt::Punct {
240 char: '#',
241 spacing: tt::Spacing::Alone,
242 span: ast_attr.pound_span,
243 }));
244 if let Some(span) = ast_attr.excl_span {
245 insert_tokens.push(tt::Leaf::Punct(tt::Punct {
246 char: '!',
247 spacing: tt::Spacing::Alone,
248 span,
249 }));
250 }
251 insert_tokens.push(tt::Leaf::Punct(tt::Punct {
252 char: '[',
253 spacing: tt::Spacing::Alone,
254 span: ast_attr.brackets_span.open,
255 }));
256
257 ast_attr.next_expanded_attr = NextExpandedAttrState::InTheMiddle;
258
259 return (true, insert_tokens);
260 } else {
261 return (false, Vec::new());
263 }
264 }
265 NextExpandedAttrState::InTheMiddle => {
266 if token_range.start() >= expanded_attr.attr.syntax().text_range().end() {
267 let insert_tokens = vec![tt::Leaf::Punct(tt::Punct {
269 char: ']',
270 spacing: tt::Spacing::Alone,
271 span: ast_attr.brackets_span.close,
272 })];
273
274 ast_attr.next_expanded_attr = NextExpandedAttrState::NotStarted;
275 ast_attr.expanded_attrs_idx += 1;
276
277 return (false, insert_tokens);
281 } else {
282 return (true, Vec::new());
284 }
285 }
286 }
287 }
288 WalkEvent::Leave(SyntaxElement::Token(_)) => {}
289 }
290 (true, Vec::new())
291 }
292}
293
294pub(crate) fn attr_macro_input_to_token_tree(
295 db: &dyn SourceDatabase,
296 node: &SyntaxNode,
297 span_map: SpanMap<'_>,
298 span: Span,
299 is_derive: bool,
300 censor_item_tree_attr_ids: &[AttrId],
301 krate: Crate,
302) -> (tt::TopSubtree, SyntaxFixupUndoInfo) {
303 let fixups = fixup::fixup_syntax(span_map, node, span, DocCommentDesugarMode::ProcMacro);
304 (
305 syntax_bridge::syntax_node_to_token_tree_modified(
306 node,
307 span_map,
308 fixups.append,
309 fixups.remove,
310 span,
311 DocCommentDesugarMode::ProcMacro,
312 macro_input_callback(db, is_derive, censor_item_tree_attr_ids, krate, span, span_map),
313 ),
314 fixups.undo_info,
315 )
316}