rustc_parse/parser/attr_wrapper.rs
1use std::borrow::Cow;
2use std::mem;
3
4use rustc_ast::token::Token;
5use rustc_ast::tokenstream::{
6 AttrsTarget, LazyAttrTokenStream, NodeRange, ParserRange, Spacing, TokenCursor,
7};
8use rustc_ast::{self as ast, AttrKind, AttrVec, Attribute, HasTokens};
9use rustc_data_structures::fx::FxHashSet;
10use rustc_errors::PResult;
11use rustc_session::parse::ParseSess;
12use rustc_span::{DUMMY_SP, sym};
13use thin_vec::ThinVec;
14
15use super::{Capturing, ForceCollect, Parser, Trailing};
16
17// When collecting tokens, this fully captures the start point. Usually its
18// just after outer attributes, but occasionally it's before.
19#[derive(Clone, Debug)]
20pub(super) struct CollectPos {
21 start_token: (Token, Spacing),
22 cursor_snapshot: TokenCursor,
23 start_pos: u32,
24}
25
26pub(super) enum UsePreAttrPos {
27 No,
28 Yes,
29}
30
31/// A wrapper type to ensure that the parser handles outer attributes correctly.
32/// When we parse outer attributes, we need to ensure that we capture tokens
33/// for the attribute target. This allows us to perform cfg-expansion on
34/// a token stream before we invoke a derive proc-macro.
35///
36/// This wrapper prevents direct access to the underlying `ast::AttrVec`.
37/// Parsing code can only get access to the underlying attributes
38/// by passing an `AttrWrapper` to `collect_tokens`.
39/// This makes it difficult to accidentally construct an AST node
40/// (which stores an `ast::AttrVec`) without first collecting tokens.
41///
42/// This struct has its own module, to ensure that the parser code
43/// cannot directly access the `attrs` field.
44#[derive(Debug, Clone)]
45pub(super) struct AttrWrapper {
46 attrs: AttrVec,
47 // The start of the outer attributes in the parser's token stream.
48 // This lets us create a `NodeReplacement` for the entire attribute
49 // target, including outer attributes. `None` if there are no outer
50 // attributes.
51 start_pos: Option<u32>,
52}
53
54impl AttrWrapper {
55 pub(super) fn new(attrs: AttrVec, start_pos: u32) -> AttrWrapper {
56 AttrWrapper { attrs, start_pos: Some(start_pos) }
57 }
58
59 pub(super) fn empty() -> AttrWrapper {
60 AttrWrapper { attrs: AttrVec::new(), start_pos: None }
61 }
62
63 pub(super) fn take_for_recovery(self, psess: &ParseSess) -> AttrVec {
64 psess.dcx().span_delayed_bug(
65 self.attrs.get(0).map(|attr| attr.span).unwrap_or(DUMMY_SP),
66 "AttrVec is taken for recovery but no error is produced",
67 );
68
69 self.attrs
70 }
71
72 /// Prepend `self.attrs` to `attrs`.
73 // FIXME: require passing an NT to prevent misuse of this method
74 pub(super) fn prepend_to_nt_inner(mut self, attrs: &mut AttrVec) {
75 mem::swap(attrs, &mut self.attrs);
76 attrs.extend(self.attrs);
77 }
78
79 pub(super) fn is_empty(&self) -> bool {
80 self.attrs.is_empty()
81 }
82}
83
84/// Returns `true` if `attrs` contains a `cfg` or `cfg_attr` attribute
85fn has_cfg_or_cfg_attr(attrs: &[Attribute]) -> bool {
86 // NOTE: Builtin attributes like `cfg` and `cfg_attr` cannot be renamed via imports.
87 // Therefore, the absence of a literal `cfg` or `cfg_attr` guarantees that
88 // we don't need to do any eager expansion.
89 attrs
90 .iter()
91 .any(|attr| attr.name().is_some_and(|ident| ident == sym::cfg || ident == sym::cfg_attr))
92}
93
94impl<'a> Parser<'a> {
95 pub(super) fn collect_pos(&self) -> CollectPos {
96 CollectPos {
97 start_token: (self.token, self.token_spacing),
98 cursor_snapshot: self.token_cursor.clone(),
99 start_pos: self.num_bump_calls,
100 }
101 }
102
103 /// Parses code with `f`. If appropriate, it records the tokens (in
104 /// `LazyAttrTokenStream` form) that were parsed in the result, accessible
105 /// via the `HasTokens` trait.
106 ///
107 /// Why is this done? Various macro cases require an AST node's tokens.
108 /// - Proc macros: all proc macros take a token stream.
109 /// - Function-style proc macros (`foo!(..)`): these can receive a token
110 /// stream without any parsing occurring within the parentheses.
111 /// - Attribute macros (`#[foo]`): at parse time (pre-resolution) we
112 /// don't know if a non-builtin `#[foo]` is an attribute proc macro, so
113 /// the parser does a full parse and collects tokens (lazily) in case.
114 /// - Derive macros (`derive(Foo)`): any input with
115 /// `#[cfg]`/`#[cfg_attr]` must be stripped before being passed to the
116 /// derive macro, which requires parsing. Identifying the end of the
117 /// item also requires parsing.
118 /// - Decl macros: a matched nonterminal (e.g. `$e:expr`) needs tokens in
119 /// case it is passed into a proc macro.
120 ///
121 /// The `Trailing` part of the callback's result indicates if an extra
122 /// token should be captured, e.g. a comma or semicolon. The
123 /// `UsePreAttrPos` part of the callback's result indicates if we should
124 /// use `pre_attr_pos` as the collection start position (only required in a
125 /// few cases).
126 ///
127 /// The `attrs` passed in are in `AttrWrapper` form, which is opaque. The
128 /// `AttrVec` within is passed to `f`. See the comment on `AttrWrapper` for
129 /// details.
130 ///
131 /// `pre_attr_pos` is the position before the outer attributes (or the node
132 /// itself, if no outer attributes are present). It is only needed if `f`
133 /// can return `UsePreAttrPos::Yes`.
134 ///
135 /// Note: If your callback consumes an opening delimiter (including the
136 /// case where `self.token` is an opening delimiter on entry to this
137 /// function), you must also consume the corresponding closing delimiter.
138 /// E.g. you can consume `something ([{ }])` or `([{}])`, but not `([{}]`.
139 /// This restriction isn't a problem in practice, because parsed AST items
140 /// always have matching delimiters.
141 ///
142 /// The following example code will be used to explain things in comments
143 /// below. It has an outer attribute and an inner attribute. Parsing it
144 /// involves two calls to this method, one of which is indirectly
145 /// recursive.
146 /// ```ignore (fake attributes)
147 /// #[cfg_eval] // token pos
148 /// mod m { // 0.. 3
149 /// #[cfg_attr(cond1, attr1)] // 3..12
150 /// fn g() { // 12..17
151 /// #![cfg_attr(cond2, attr2)] // 17..27
152 /// let _x = 3; // 27..32
153 /// } // 32..33
154 /// } // 33..34
155 /// ```
156 pub(super) fn collect_tokens<R: HasTokens>(
157 &mut self,
158 pre_attr_pos: Option<CollectPos>,
159 attrs: AttrWrapper,
160 force_collect: ForceCollect,
161 f: impl FnOnce(&mut Self, AttrVec) -> PResult<'a, (R, Trailing, UsePreAttrPos)>,
162 ) -> PResult<'a, R> {
163 let possible_capture_mode = self.capture_cfg;
164
165 // We must collect if anything could observe the collected tokens, i.e.
166 // if any of the following conditions hold.
167 // - We are force collecting tokens (because force collection requires
168 // tokens by definition).
169 let needs_collection = matches!(force_collect, ForceCollect::Yes)
170 // - Any of our outer attributes require tokens.
171 || needs_tokens(&attrs.attrs)
172 // - Our target supports custom inner attributes (custom
173 // inner attribute invocation might require token capturing).
174 || R::SUPPORTS_CUSTOM_INNER_ATTRS
175 // - We are in "possible capture mode" (which requires tokens if
176 // the parsed node has `#[cfg]` or `#[cfg_attr]` attributes).
177 || possible_capture_mode;
178 if !needs_collection {
179 return Ok(f(self, attrs.attrs)?.0);
180 }
181
182 let mut collect_pos = self.collect_pos();
183 let has_outer_attrs = !attrs.attrs.is_empty();
184 let parser_replacements_start = self.capture_state.parser_replacements.len();
185
186 // We set and restore `Capturing::Yes` on either side of the call to
187 // `f`, so we can distinguish the outermost call to `collect_tokens`
188 // (e.g. parsing `m` in the example above) from any inner (indirectly
189 // recursive) calls (e.g. parsing `g` in the example above). This
190 // distinction is used below and in `Parser::parse_inner_attributes`.
191 let (mut ret, capture_trailing, use_pre_attr_pos) = {
192 let prev_capturing = mem::replace(&mut self.capture_state.capturing, Capturing::Yes);
193 let res = f(self, attrs.attrs);
194 self.capture_state.capturing = prev_capturing;
195 res?
196 };
197
198 // - `None`: Our target doesn't support tokens at all (e.g. `NtIdent`).
199 // - `Some(None)`: Our target supports tokens and has none.
200 // - `Some(Some(_))`: Our target already has tokens set (e.g. we've
201 // parsed something like `#[my_attr] $item`).
202 let ret_can_hold_tokens = matches!(ret.tokens_mut(), Some(None));
203
204 // Ignore any attributes we've previously processed. This happens when
205 // an inner call to `collect_tokens` returns an AST node and then an
206 // outer call ends up with the same AST node without any additional
207 // wrapping layer.
208 let mut seen_indices = FxHashSet::default();
209 for (i, attr) in ret.attrs().iter().enumerate() {
210 let is_unseen = self.capture_state.seen_attrs.insert(attr.id);
211 if !is_unseen {
212 seen_indices.insert(i);
213 }
214 }
215 let ret_attrs: Cow<'_, [Attribute]> =
216 if seen_indices.is_empty() {
217 Cow::Borrowed(ret.attrs())
218 } else {
219 let ret_attrs =
220 ret.attrs()
221 .iter()
222 .enumerate()
223 .filter_map(|(i, attr)| {
224 if seen_indices.contains(&i) { None } else { Some(attr.clone()) }
225 })
226 .collect();
227 Cow::Owned(ret_attrs)
228 };
229
230 // When we're not in "definite capture mode", then skip collecting and
231 // return early if `ret` doesn't support tokens or already has some.
232 //
233 // Note that this check is independent of `force_collect`. There's no
234 // need to collect tokens when we don't support tokens or already have
235 // tokens.
236 let definite_capture_mode = self.capture_cfg
237 && matches!(self.capture_state.capturing, Capturing::Yes)
238 && has_cfg_or_cfg_attr(&ret_attrs);
239 if !definite_capture_mode && !ret_can_hold_tokens {
240 return Ok(ret);
241 }
242
243 // This is similar to the `needs_collection` check at the start of this
244 // function, but now that we've parsed an AST node we have complete
245 // information available. (If we return early here that means the
246 // setup, such as cloning the token cursor, was unnecessary. That's
247 // hard to avoid.)
248 //
249 // We must collect if anything could observe the collected tokens, i.e.
250 // if any of the following conditions hold.
251 // - We are force collecting tokens.
252 let needs_collection = matches!(force_collect, ForceCollect::Yes)
253 // - Any of our outer *or* inner attributes require tokens.
254 // (`attr.attrs` was just outer attributes, but `ret.attrs()` is
255 // outer and inner attributes. So this check is more precise than
256 // the earlier `needs_tokens` check, and we don't need to
257 // check `R::SUPPORTS_CUSTOM_INNER_ATTRS`.)
258 || needs_tokens(&ret_attrs)
259 // - We are in "definite capture mode", which requires that there
260 // are `#[cfg]` or `#[cfg_attr]` attributes. (During normal
261 // non-`capture_cfg` parsing, we don't need any special capturing
262 // for those attributes, because they're builtin.)
263 || definite_capture_mode;
264 if !needs_collection {
265 return Ok(ret);
266 }
267
268 // Replace the post-attribute collection start position with the
269 // pre-attribute position supplied, if `f` indicated it is necessary.
270 // (The caller is responsible for providing a non-`None` `pre_attr_pos`
271 // if this is a possibility.)
272 if matches!(use_pre_attr_pos, UsePreAttrPos::Yes) {
273 collect_pos = pre_attr_pos.unwrap();
274 }
275
276 let parser_replacements_end = self.capture_state.parser_replacements.len();
277
278 assert!(
279 !(self.break_last_token > 0 && matches!(capture_trailing, Trailing::Yes)),
280 "Cannot have break_last_token > 0 and have trailing token"
281 );
282 assert!(self.break_last_token <= 2, "cannot break token more than twice");
283
284 let end_pos = self.num_bump_calls
285 + capture_trailing as u32
286 // If we "broke" the last token (e.g. breaking a `>>` token once into `>` + `>`, or
287 // breaking a `>>=` token twice into `>` + `>` + `=`), then extend the range of
288 // captured tokens to include it, because the parser was not actually bumped past it.
289 // (Even if we broke twice, it was still just one token originally, hence the `1`.)
290 // When the `LazyAttrTokenStream` gets converted into an `AttrTokenStream`, we will
291 // rebreak that final token once or twice.
292 + if self.break_last_token == 0 { 0 } else { 1 };
293
294 let num_calls = end_pos - collect_pos.start_pos;
295
296 // Take the captured `ParserRange`s for any inner attributes that we parsed in
297 // `Parser::parse_inner_attributes`, and pair them in a `ParserReplacement` with `None`,
298 // which means the relevant tokens will be removed. (More details below.)
299 let mut inner_attr_parser_replacements = Vec::new();
300 for attr in ret_attrs.iter() {
301 if attr.style == ast::AttrStyle::Inner {
302 if let Some(inner_attr_parser_range) =
303 self.capture_state.inner_attr_parser_ranges.remove(&attr.id)
304 {
305 inner_attr_parser_replacements.push((inner_attr_parser_range, None));
306 } else {
307 self.dcx().span_delayed_bug(attr.span, "Missing token range for attribute");
308 }
309 }
310 }
311
312 // This is hot enough for `deep-vector` that checking the conditions for an empty iterator
313 // is measurably faster than actually executing the iterator.
314 let node_replacements = if parser_replacements_start == parser_replacements_end
315 && inner_attr_parser_replacements.is_empty()
316 {
317 ThinVec::new()
318 } else {
319 // Grab any replace ranges that occur *inside* the current AST node. Convert them
320 // from `ParserRange` form to `NodeRange` form. We will perform the actual
321 // replacement only when we convert the `LazyAttrTokenStream` to an
322 // `AttrTokenStream`.
323 self.capture_state.parser_replacements
324 [parser_replacements_start..parser_replacements_end]
325 .iter()
326 .cloned()
327 .chain(inner_attr_parser_replacements)
328 .map(|(parser_range, data)| {
329 (NodeRange::new(parser_range, collect_pos.start_pos), data)
330 })
331 .collect()
332 };
333
334 // What is the status here when parsing the example code at the top of this method?
335 //
336 // When parsing `g`:
337 // - `start_pos..end_pos` is `12..33` (`fn g { ... }`, excluding the outer attr).
338 // - `inner_attr_parser_replacements` has one entry (`ParserRange(17..27)`), to
339 // delete the inner attr's tokens.
340 // - This entry is converted to `NodeRange(5..15)` (relative to the `fn`) and put into
341 // the lazy tokens for `g`, i.e. deleting the inner attr from those tokens (if they get
342 // evaluated).
343 // - Those lazy tokens are also put into an `AttrsTarget` that is appended to `self`'s
344 // replace ranges at the bottom of this function, for processing when parsing `m`.
345 // - `parser_replacements_start..parser_replacements_end` is empty.
346 //
347 // When parsing `m`:
348 // - `start_pos..end_pos` is `0..34` (`mod m`, excluding the `#[cfg_eval]` attribute).
349 // - `inner_attr_parser_replacements` is empty.
350 // - `parser_replacements_start..parser_replacements_end` has one entry.
351 // - One `AttrsTarget` (added below when parsing `g`) to replace all of `g` (`3..33`,
352 // including its outer attribute), with:
353 // - `attrs`: includes the outer and the inner attr.
354 // - `tokens`: lazy tokens for `g` (with its inner attr deleted).
355
356 let tokens = LazyAttrTokenStream::new_pending(
357 collect_pos.start_token,
358 collect_pos.cursor_snapshot,
359 num_calls,
360 self.break_last_token,
361 node_replacements,
362 );
363 let mut tokens_used = false;
364
365 // If in "definite capture mode" we need to register a replace range
366 // for the `#[cfg]` and/or `#[cfg_attr]` attrs. This allows us to run
367 // eager cfg-expansion on the captured token stream.
368 if definite_capture_mode {
369 assert!(self.break_last_token == 0, "Should not have unglued last token with cfg attr");
370
371 // What is the status here when parsing the example code at the top of this method?
372 //
373 // When parsing `g`, we add one entry:
374 // - The pushed entry (`ParserRange(3..33)`) has a new `AttrsTarget` with:
375 // - `attrs`: includes the outer and the inner attr.
376 // - `tokens`: lazy tokens for `g` (with its inner attr deleted).
377 //
378 // When parsing `m`, we do nothing here.
379
380 // Set things up so that the entire AST node that we just parsed, including attributes,
381 // will be replaced with `target` in the lazy token stream. This will allow us to
382 // cfg-expand this AST node.
383 let start_pos =
384 if has_outer_attrs { attrs.start_pos.unwrap() } else { collect_pos.start_pos };
385 let target =
386 AttrsTarget { attrs: ret_attrs.iter().cloned().collect(), tokens: tokens.clone() };
387 tokens_used = true;
388 self.capture_state
389 .parser_replacements
390 .push((ParserRange(start_pos..end_pos), Some(target)));
391 } else if matches!(self.capture_state.capturing, Capturing::No) {
392 // Only clear the ranges once we've finished capturing entirely, i.e. we've finished
393 // the outermost call to this method.
394 self.capture_state.parser_replacements.clear();
395 self.capture_state.inner_attr_parser_ranges.clear();
396 self.capture_state.seen_attrs.clear();
397 }
398
399 // If we support tokens and don't already have them, store the newly captured tokens.
400 if let Some(target_tokens @ None) = ret.tokens_mut() {
401 tokens_used = true;
402 *target_tokens = Some(tokens);
403 }
404
405 assert!(tokens_used); // check we didn't create `tokens` unnecessarily
406 Ok(ret)
407 }
408}
409
410fn needs_tokens(attrs: &[ast::Attribute]) -> bool {
411 // Tokens are needed if...
412 attrs.iter().any(|attr| match &attr.kind {
413 AttrKind::Normal(normal) => {
414 match normal.item.name() {
415 // ... a multi-segment attribute is present, e.g. `rustfmt::skip`.
416 None => true,
417 // ... `cfg_attr` or a single-segment non-builtin attribute is present, e.g.
418 // `derive`, `test`, `global_allocator`.
419 Some(name) => name == sym::cfg_attr || !rustc_feature::is_builtin_attr_name(name),
420 }
421 }
422 // Synthetic attributes are created only during expansion, and can't re-enter the parser
423 // because they have no token form.
424 AttrKind::Synthetic(_) => unreachable!(),
425 AttrKind::DocComment(..) => false,
426 })
427}