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, AttrVec, Attribute, HasAttrs, 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.iter().any(|attr| {
90        attr.ident().is_some_and(|ident| ident.name == sym::cfg || ident.name == sym::cfg_attr)
91    })
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. The `Trailing` part of the callback's
106    /// result indicates if an extra token should be captured, e.g. a comma or
107    /// semicolon. The `UsePreAttrPos` part of the callback's result indicates
108    /// if we should use `pre_attr_pos` as the collection start position (only
109    /// required in a few cases).
110    ///
111    /// The `attrs` passed in are in `AttrWrapper` form, which is opaque. The
112    /// `AttrVec` within is passed to `f`. See the comment on `AttrWrapper` for
113    /// details.
114    ///
115    /// `pre_attr_pos` is the position before the outer attributes (or the node
116    /// itself, if no outer attributes are present). It is only needed if `f`
117    /// can return `UsePreAttrPos::Yes`.
118    ///
119    /// Note: If your callback consumes an opening delimiter (including the
120    /// case where `self.token` is an opening delimiter on entry to this
121    /// function), you must also consume the corresponding closing delimiter.
122    /// E.g. you can consume `something ([{ }])` or `([{}])`, but not `([{}]`.
123    /// This restriction isn't a problem in practice, because parsed AST items
124    /// always have matching delimiters.
125    ///
126    /// The following example code will be used to explain things in comments
127    /// below. It has an outer attribute and an inner attribute. Parsing it
128    /// involves two calls to this method, one of which is indirectly
129    /// recursive.
130    /// ```ignore (fake attributes)
131    /// #[cfg_eval]                         // token pos
132    /// mod m {                             //   0.. 3
133    ///     #[cfg_attr(cond1, attr1)]       //   3..12
134    ///     fn g() {                        //  12..17
135    ///         #![cfg_attr(cond2, attr2)]  //  17..27
136    ///         let _x = 3;                 //  27..32
137    ///     }                               //  32..33
138    /// }                                   //  33..34
139    /// ```
140    pub(super) fn collect_tokens<R: HasAttrs + HasTokens>(
141        &mut self,
142        pre_attr_pos: Option<CollectPos>,
143        attrs: AttrWrapper,
144        force_collect: ForceCollect,
145        f: impl FnOnce(&mut Self, AttrVec) -> PResult<'a, (R, Trailing, UsePreAttrPos)>,
146    ) -> PResult<'a, R> {
147        let possible_capture_mode = self.capture_cfg;
148
149        // We must collect if anything could observe the collected tokens, i.e.
150        // if any of the following conditions hold.
151        // - We are force collecting tokens (because force collection requires
152        //   tokens by definition).
153        let needs_collection = matches!(force_collect, ForceCollect::Yes)
154            // - Any of our outer attributes require tokens.
155            || needs_tokens(&attrs.attrs)
156            // - Our target supports custom inner attributes (custom
157            //   inner attribute invocation might require token capturing).
158            || R::SUPPORTS_CUSTOM_INNER_ATTRS
159            // - We are in "possible capture mode" (which requires tokens if
160            //   the parsed node has `#[cfg]` or `#[cfg_attr]` attributes).
161            || possible_capture_mode;
162        if !needs_collection {
163            return Ok(f(self, attrs.attrs)?.0);
164        }
165
166        let mut collect_pos = self.collect_pos();
167        let has_outer_attrs = !attrs.attrs.is_empty();
168        let parser_replacements_start = self.capture_state.parser_replacements.len();
169
170        // We set and restore `Capturing::Yes` on either side of the call to
171        // `f`, so we can distinguish the outermost call to `collect_tokens`
172        // (e.g. parsing `m` in the example above) from any inner (indirectly
173        // recursive) calls (e.g. parsing `g` in the example above). This
174        // distinction is used below and in `Parser::parse_inner_attributes`.
175        let (mut ret, capture_trailing, use_pre_attr_pos) = {
176            let prev_capturing = mem::replace(&mut self.capture_state.capturing, Capturing::Yes);
177            let res = f(self, attrs.attrs);
178            self.capture_state.capturing = prev_capturing;
179            res?
180        };
181
182        // - `None`: Our target doesn't support tokens at all (e.g. `NtIdent`).
183        // - `Some(None)`: Our target supports tokens and has none.
184        // - `Some(Some(_))`: Our target already has tokens set (e.g. we've
185        //   parsed something like `#[my_attr] $item`).
186        let ret_can_hold_tokens = matches!(ret.tokens_mut(), Some(None));
187
188        // Ignore any attributes we've previously processed. This happens when
189        // an inner call to `collect_tokens` returns an AST node and then an
190        // outer call ends up with the same AST node without any additional
191        // wrapping layer.
192        let mut seen_indices = FxHashSet::default();
193        for (i, attr) in ret.attrs().iter().enumerate() {
194            let is_unseen = self.capture_state.seen_attrs.insert(attr.id);
195            if !is_unseen {
196                seen_indices.insert(i);
197            }
198        }
199        let ret_attrs: Cow<'_, [Attribute]> =
200            if seen_indices.is_empty() {
201                Cow::Borrowed(ret.attrs())
202            } else {
203                let ret_attrs =
204                    ret.attrs()
205                        .iter()
206                        .enumerate()
207                        .filter_map(|(i, attr)| {
208                            if seen_indices.contains(&i) { None } else { Some(attr.clone()) }
209                        })
210                        .collect();
211                Cow::Owned(ret_attrs)
212            };
213
214        // When we're not in "definite capture mode", then skip collecting and
215        // return early if `ret` doesn't support tokens or already has some.
216        //
217        // Note that this check is independent of `force_collect`. There's no
218        // need to collect tokens when we don't support tokens or already have
219        // tokens.
220        let definite_capture_mode = self.capture_cfg
221            && matches!(self.capture_state.capturing, Capturing::Yes)
222            && has_cfg_or_cfg_attr(&ret_attrs);
223        if !definite_capture_mode && !ret_can_hold_tokens {
224            return Ok(ret);
225        }
226
227        // This is similar to the `needs_collection` check at the start of this
228        // function, but now that we've parsed an AST node we have complete
229        // information available. (If we return early here that means the
230        // setup, such as cloning the token cursor, was unnecessary. That's
231        // hard to avoid.)
232        //
233        // We must collect if anything could observe the collected tokens, i.e.
234        // if any of the following conditions hold.
235        // - We are force collecting tokens.
236        let needs_collection = matches!(force_collect, ForceCollect::Yes)
237            // - Any of our outer *or* inner attributes require tokens.
238            //   (`attr.attrs` was just outer attributes, but `ret.attrs()` is
239            //   outer and inner attributes. So this check is more precise than
240            //   the earlier `needs_tokens` check, and we don't need to
241            //   check `R::SUPPORTS_CUSTOM_INNER_ATTRS`.)
242            || needs_tokens(&ret_attrs)
243            // - We are in "definite capture mode", which requires that there
244            //   are `#[cfg]` or `#[cfg_attr]` attributes. (During normal
245            //   non-`capture_cfg` parsing, we don't need any special capturing
246            //   for those attributes, because they're builtin.)
247            || definite_capture_mode;
248        if !needs_collection {
249            return Ok(ret);
250        }
251
252        // Replace the post-attribute collection start position with the
253        // pre-attribute position supplied, if `f` indicated it is necessary.
254        // (The caller is responsible for providing a non-`None` `pre_attr_pos`
255        // if this is a possibility.)
256        if matches!(use_pre_attr_pos, UsePreAttrPos::Yes) {
257            collect_pos = pre_attr_pos.unwrap();
258        }
259
260        let parser_replacements_end = self.capture_state.parser_replacements.len();
261
262        assert!(
263            !(self.break_last_token > 0 && matches!(capture_trailing, Trailing::Yes)),
264            "Cannot have break_last_token > 0 and have trailing token"
265        );
266        assert!(self.break_last_token <= 2, "cannot break token more than twice");
267
268        let end_pos = self.num_bump_calls
269            + capture_trailing as u32
270            // If we "broke" the last token (e.g. breaking a `>>` token once into `>` + `>`, or
271            // breaking a `>>=` token twice into `>` + `>` + `=`), then extend the range of
272            // captured tokens to include it, because the parser was not actually bumped past it.
273            // (Even if we broke twice, it was still just one token originally, hence the `1`.)
274            // When the `LazyAttrTokenStream` gets converted into an `AttrTokenStream`, we will
275            // rebreak that final token once or twice.
276            + if self.break_last_token == 0 { 0 } else { 1 };
277
278        let num_calls = end_pos - collect_pos.start_pos;
279
280        // Take the captured `ParserRange`s for any inner attributes that we parsed in
281        // `Parser::parse_inner_attributes`, and pair them in a `ParserReplacement` with `None`,
282        // which means the relevant tokens will be removed. (More details below.)
283        let mut inner_attr_parser_replacements = Vec::new();
284        for attr in ret_attrs.iter() {
285            if attr.style == ast::AttrStyle::Inner {
286                if let Some(inner_attr_parser_range) =
287                    self.capture_state.inner_attr_parser_ranges.remove(&attr.id)
288                {
289                    inner_attr_parser_replacements.push((inner_attr_parser_range, None));
290                } else {
291                    self.dcx().span_delayed_bug(attr.span, "Missing token range for attribute");
292                }
293            }
294        }
295
296        // This is hot enough for `deep-vector` that checking the conditions for an empty iterator
297        // is measurably faster than actually executing the iterator.
298        let node_replacements = if parser_replacements_start == parser_replacements_end
299            && inner_attr_parser_replacements.is_empty()
300        {
301            ThinVec::new()
302        } else {
303            // Grab any replace ranges that occur *inside* the current AST node. Convert them
304            // from `ParserRange` form to `NodeRange` form. We will perform the actual
305            // replacement only when we convert the `LazyAttrTokenStream` to an
306            // `AttrTokenStream`.
307            self.capture_state.parser_replacements
308                [parser_replacements_start..parser_replacements_end]
309                .iter()
310                .cloned()
311                .chain(inner_attr_parser_replacements)
312                .map(|(parser_range, data)| {
313                    (NodeRange::new(parser_range, collect_pos.start_pos), data)
314                })
315                .collect()
316        };
317
318        // What is the status here when parsing the example code at the top of this method?
319        //
320        // When parsing `g`:
321        // - `start_pos..end_pos` is `12..33` (`fn g { ... }`, excluding the outer attr).
322        // - `inner_attr_parser_replacements` has one entry (`ParserRange(17..27)`), to
323        //   delete the inner attr's tokens.
324        //   - This entry is converted to `NodeRange(5..15)` (relative to the `fn`) and put into
325        //     the lazy tokens for `g`, i.e. deleting the inner attr from those tokens (if they get
326        //     evaluated).
327        //   - Those lazy tokens are also put into an `AttrsTarget` that is appended to `self`'s
328        //     replace ranges at the bottom of this function, for processing when parsing `m`.
329        // - `parser_replacements_start..parser_replacements_end` is empty.
330        //
331        // When parsing `m`:
332        // - `start_pos..end_pos` is `0..34` (`mod m`, excluding the `#[cfg_eval]` attribute).
333        // - `inner_attr_parser_replacements` is empty.
334        // - `parser_replacements_start..parser_replacements_end` has one entry.
335        //   - One `AttrsTarget` (added below when parsing `g`) to replace all of `g` (`3..33`,
336        //     including its outer attribute), with:
337        //     - `attrs`: includes the outer and the inner attr.
338        //     - `tokens`: lazy tokens for `g` (with its inner attr deleted).
339
340        let tokens = LazyAttrTokenStream::new_pending(
341            collect_pos.start_token,
342            collect_pos.cursor_snapshot,
343            num_calls,
344            self.break_last_token,
345            node_replacements,
346        );
347        let mut tokens_used = false;
348
349        // If in "definite capture mode" we need to register a replace range
350        // for the `#[cfg]` and/or `#[cfg_attr]` attrs. This allows us to run
351        // eager cfg-expansion on the captured token stream.
352        if definite_capture_mode {
353            assert!(self.break_last_token == 0, "Should not have unglued last token with cfg attr");
354
355            // What is the status here when parsing the example code at the top of this method?
356            //
357            // When parsing `g`, we add one entry:
358            // - The pushed entry (`ParserRange(3..33)`) has a new `AttrsTarget` with:
359            //   - `attrs`: includes the outer and the inner attr.
360            //   - `tokens`: lazy tokens for `g` (with its inner attr deleted).
361            //
362            // When parsing `m`, we do nothing here.
363
364            // Set things up so that the entire AST node that we just parsed, including attributes,
365            // will be replaced with `target` in the lazy token stream. This will allow us to
366            // cfg-expand this AST node.
367            let start_pos =
368                if has_outer_attrs { attrs.start_pos.unwrap() } else { collect_pos.start_pos };
369            let target =
370                AttrsTarget { attrs: ret_attrs.iter().cloned().collect(), tokens: tokens.clone() };
371            tokens_used = true;
372            self.capture_state
373                .parser_replacements
374                .push((ParserRange(start_pos..end_pos), Some(target)));
375        } else if matches!(self.capture_state.capturing, Capturing::No) {
376            // Only clear the ranges once we've finished capturing entirely, i.e. we've finished
377            // the outermost call to this method.
378            self.capture_state.parser_replacements.clear();
379            self.capture_state.inner_attr_parser_ranges.clear();
380            self.capture_state.seen_attrs.clear();
381        }
382
383        // If we support tokens and don't already have them, store the newly captured tokens.
384        if let Some(target_tokens @ None) = ret.tokens_mut() {
385            tokens_used = true;
386            *target_tokens = Some(tokens);
387        }
388
389        assert!(tokens_used); // check we didn't create `tokens` unnecessarily
390        Ok(ret)
391    }
392}
393
394/// Tokens are needed if:
395/// - any non-single-segment attributes (other than doc comments) are present,
396///   e.g. `rustfmt::skip`; or
397/// - any `cfg_attr` attributes are present; or
398/// - any single-segment, non-builtin attributes are present, e.g. `derive`,
399///   `test`, `global_allocator`.
400fn needs_tokens(attrs: &[ast::Attribute]) -> bool {
401    attrs.iter().any(|attr| match attr.ident() {
402        None => !attr.is_doc_comment(),
403        Some(ident) => {
404            ident.name == sym::cfg_attr || !rustc_feature::is_builtin_attr_name(ident.name)
405        }
406    })
407}
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy