Skip to main content

oxc_parser/embedding/
projection.rs

1//! Move an authenticated selected header into its original arena, once.
2
3use oxc_ast::ast::{Expression, FormalParameters, FunctionBody, Statement};
4use oxc_diagnostics::Diagnostics;
5use oxc_span::{GetSpan, SourceType, Span};
6
7use super::{EmbeddingGoal, EmbeddingHole, EmbeddingObservation};
8use crate::ParseOptions;
9
10macro_rules! projection {
11    ($owner:ident, $proof:ident, $root:ty, $accessor:ident) => {
12        /// An ordinarily owned complete parser observation and selected arena root.
13        pub struct $owner<'a> {
14            observation: EmbeddingObservation<'a>,
15            root: Option<&'a $root>,
16        }
17        impl core::fmt::Debug for $owner<'_> {
18            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
19                f.debug_tuple(stringify!($owner)).field(&self.observation).finish()
20            }
21        }
22        /// A short private-origin borrow; raw ASTs and numeric windows cannot mint it.
23        pub struct $proof<'p, 'a> {
24            owner: &'p $owner<'a>,
25            root: &'a $root,
26        }
27        impl<'a> $owner<'a> {
28            #[must_use]
29            pub fn admitted(&self) -> Option<$proof<'_, 'a>> {
30                self.root
31                    .filter(|_| self.observation.hole.is_none())
32                    .map(|root| $proof { owner: self, root })
33            }
34            #[must_use]
35            pub const fn hole(&self) -> Option<EmbeddingHole> {
36                self.observation.hole
37            }
38            #[must_use]
39            pub fn diagnostics(&self) -> &Diagnostics {
40                self.observation.diagnostics()
41            }
42            #[must_use]
43            pub fn comments(&self) -> &[oxc_ast::ast::Comment] {
44                self.observation.comments()
45            }
46            #[must_use]
47            pub const fn content(&self) -> &'a str {
48                self.observation.content
49            }
50            #[must_use]
51            pub const fn source_type(&self) -> SourceType {
52                self.observation.source_type
53            }
54            #[must_use]
55            pub const fn parser_content_span(&self) -> Span {
56                self.observation.content_span
57            }
58        }
59        impl<'p, 'a> $proof<'p, 'a> {
60            #[must_use]
61            pub const fn $accessor(&self) -> &'a $root {
62                self.root
63            }
64            #[must_use]
65            pub const fn content(&self) -> &'a str {
66                self.owner.observation.content
67            }
68            #[must_use]
69            pub const fn parser_content_span(&self) -> Span {
70                self.owner.observation.content_span
71            }
72            #[must_use]
73            pub fn parser_container_span(&self) -> Span {
74                self.root.span()
75            }
76            #[must_use]
77            pub const fn source_type(&self) -> SourceType {
78                self.owner.observation.source_type
79            }
80            #[must_use]
81            pub const fn options(&self) -> ParseOptions {
82                self.owner.observation.options
83            }
84        }
85    };
86}
87
88projection!(ExpressionObservation, AdmittedExpression, Expression<'a>, expression);
89projection!(HandlerBodyObservation, AdmittedHandlerBody, FunctionBody<'a>, body);
90projection!(ParametersObservation, AdmittedParameters, FormalParameters<'a>, parameters);
91
92impl AdmittedExpression<'_, '_> {
93    /// Whether the original stock lexer decoded a legacy numeric literal or
94    /// string escape forbidden in a strict module. This borrows the complete
95    /// original parser observation; it adds no parse or source/AST scan.
96    /// Syntax admission is unchanged, and false does not certify semantics.
97    #[must_use]
98    pub const fn has_legacy_literals(&self) -> bool {
99        self.owner.observation.parsed.has_legacy_literals
100    }
101}
102
103impl<'a> EmbeddingObservation<'a> {
104    /// Consumption uses only the original stored arena, not a caller arena.
105    ///
106    /// ```compile_fail,E0061
107    /// use oxc_allocator::Allocator;
108    /// use oxc_parser::EmbeddingObservation;
109    /// fn substitute<'a>(owner: EmbeddingObservation<'a>, other: &'a Allocator) {
110    ///     owner.into_expression(other);
111    /// }
112    /// ```
113    ///
114    /// ```compile_fail,E0451
115    /// use oxc_ast::ast::Expression;
116    /// use oxc_parser::{AdmittedExpression, ExpressionObservation};
117    /// fn forge<'p, 'a>(owner: &'p ExpressionObservation<'a>, root: &'a Expression<'a>) -> AdmittedExpression<'p, 'a> {
118    ///     AdmittedExpression { owner, root }
119    /// }
120    /// ```
121    pub fn into_expression(mut self) -> Result<ExpressionObservation<'a>, Box<Self>> {
122        if self.goal != EmbeddingGoal::Expr {
123            return Err(Box::new(self));
124        }
125        let root = if self.hole.is_none()
126            && let Some(Statement::ExpressionStatement(statement)) = self.parsed.program.body.pop()
127            && let Expression::ParenthesizedExpression(wrapper) = statement.unbox().expression
128        {
129            Some(&*self.allocator.alloc(wrapper.unbox().expression))
130        } else {
131            None
132        };
133        if self.hole.is_none() && root.is_none() {
134            self.hole = Some(EmbeddingHole::InvalidExpressionShape);
135        }
136        Ok(ExpressionObservation { observation: self, root })
137    }
138
139    /// Raw FunctionBody roots cannot mint original-parser authority.
140    ///
141    /// ```compile_fail,E0451
142    /// use oxc_ast::ast::FunctionBody;
143    /// use oxc_parser::{AdmittedHandlerBody, HandlerBodyObservation};
144    /// fn forge<'p, 'a>(owner: &'p HandlerBodyObservation<'a>, root: &'a FunctionBody<'a>) -> AdmittedHandlerBody<'p, 'a> {
145    ///     AdmittedHandlerBody { owner, root }
146    /// }
147    /// ```
148    ///
149    /// ```compile_fail,E0308
150    /// use oxc_ast::ast::FunctionBody;
151    /// use oxc_parser::AdmittedHandlerBody;
152    /// fn mutate<'p, 'a>(token: AdmittedHandlerBody<'p, 'a>) -> &'a mut FunctionBody<'a> {
153    ///     token.body()
154    /// }
155    /// ```
156    pub fn into_handler_body(mut self) -> Result<HandlerBodyObservation<'a>, Box<Self>> {
157        if self.goal != EmbeddingGoal::HandlerBody {
158            return Err(Box::new(self));
159        }
160        let root = if self.hole.is_none()
161            && let Some(Statement::ExpressionStatement(statement)) = self.parsed.program.body.pop()
162            && let Expression::ArrowFunctionExpression(arrow) = statement.unbox().expression
163        {
164            Some(&*self.allocator.alloc(arrow.unbox().body.unbox()))
165        } else {
166            None
167        };
168        if self.hole.is_none() && root.is_none() {
169            self.hole = Some(EmbeddingHole::InvalidWrappedShape);
170        }
171        Ok(HandlerBodyObservation { observation: self, root })
172    }
173
174    /// Raw formal parameters cannot replace the original selected owner.
175    ///
176    /// ```compile_fail,E0451
177    /// use oxc_ast::ast::FormalParameters;
178    /// use oxc_parser::{AdmittedParameters, ParametersObservation};
179    /// fn forge<'p, 'a>(owner: &'p ParametersObservation<'a>, root: &'a FormalParameters<'a>) -> AdmittedParameters<'p, 'a> {
180    ///     AdmittedParameters { owner, root }
181    /// }
182    /// ```
183    pub fn into_parameters(mut self) -> Result<ParametersObservation<'a>, Box<Self>> {
184        if self.goal != EmbeddingGoal::Parameters {
185            return Err(Box::new(self));
186        }
187        let root = if self.hole.is_none()
188            && let Some(Statement::ExpressionStatement(statement)) = self.parsed.program.body.pop()
189            && let Expression::ArrowFunctionExpression(arrow) = statement.unbox().expression
190        {
191            Some(&*self.allocator.alloc(arrow.unbox().params.unbox()))
192        } else {
193            None
194        };
195        if self.hole.is_none() && root.is_none() {
196            self.hole = Some(EmbeddingHole::InvalidWrappedShape);
197        }
198        Ok(ParametersObservation { observation: self, root })
199    }
200}