biome_parser/
parsed_syntax.rs

1use crate::parse_recovery::{ParseRecovery, ParseRecoveryTokenSet, RecoveryResult};
2use crate::parsed_syntax::ParsedSyntax::{Absent, Present};
3use crate::prelude::*;
4use biome_rowan::TextRange;
5
6/// Syntax that is either present in the source tree or absent.
7///
8/// This type is commonly used as the return type of parse functions with the following types
9///
10///
11/// ## Parse Rule conventions
12///
13/// * A parse rule must return [ParsedSyntax::Present] if it is able to parse a node or at least parts of it. For example,
14///     the `parse_for_statement` should return [ParsedSyntax::Present] for `for (` even tough many of the required children are missing
15///     because it is still able to parse parts of the for statement.
16/// * A parse rule must return [ParsedSyntax::Absent] if the expected node isn't present in the source code.
17///     In most cases, this means if the first expected token isn't present, for example,
18///     if the `for` keyword isn't present when parsing a for statement.
19///
20/// However, it can be possible for rules to recover even if the first token doesn't match. One example
21/// is when parsing an assignment target that has an optional default. The rule can recover even
22/// if the assignment target is missing as long as the cursor is then positioned at an `=` token.
23///
24/// The rule must then return [ParsedSyntax::Present] with the partial parsed node.
25/// * A parse rule must not eat any tokens when it returns [ParsedSyntax::Absent]
26/// * A parse rule must not add any errors when it returns [ParsedSyntax::Absent]
27///
28/// This is a custom enum over using `Option` because [ParsedSyntax::Absent] values must be handled by the caller.
29#[derive(Debug, PartialEq, Eq)]
30#[must_use = "this `ParsedSyntax` may be an `Absent` variant, which should be handled"]
31pub enum ParsedSyntax {
32    /// A syntax that isn't present in the source code. Used when a parse rule can't match the current
33    /// token of the parser.
34    Absent,
35
36    /// A completed syntax node with all or some of its children.
37    Present(CompletedMarker),
38}
39
40impl ParsedSyntax {
41    /// Converts from `ParsedSyntax` to `Option<CompletedMarker>`.
42    ///
43    /// Converts `self` into an `Option<CompletedMarker>`, consuming `self`
44    #[inline]
45    pub fn ok(self) -> Option<CompletedMarker> {
46        match self {
47            Absent => None,
48            Present(marker) => Some(marker),
49        }
50    }
51
52    /// Calls `op` if the syntax is present and otherwise returns [ParsedSyntax::Absent]
53    #[inline]
54    pub fn and_then<F>(self, op: F) -> ParsedSyntax
55    where
56        F: FnOnce(CompletedMarker) -> ParsedSyntax,
57    {
58        match self {
59            Absent => Absent,
60            Present(marker) => op(marker),
61        }
62    }
63
64    /// Calls `op` if the syntax is absent and otherwise returns [ParsedSyntax::Present]
65    #[inline]
66    pub fn or_else<F>(self, op: F) -> ParsedSyntax
67    where
68        F: FnOnce() -> ParsedSyntax,
69    {
70        match self {
71            Absent => op(),
72            t => t,
73        }
74    }
75
76    /// Returns `true` if the parsed syntax is [ParsedSyntax::Present]
77    #[inline]
78    #[must_use]
79    pub fn is_present(&self) -> bool {
80        matches!(self, Present(_))
81    }
82
83    /// Returns `true` if the parsed syntax is [ParsedSyntax::Absent]
84    #[inline]
85    #[must_use]
86    pub fn is_absent(&self) -> bool {
87        matches!(self, Absent)
88    }
89
90    /// It returns the contained [ParsedSyntax::Present] value, consuming the `self` value
91    ///
92    /// # Panics
93    ///
94    ///  Panics if the current syntax is [ParsedSyntax::Absent]
95    #[inline]
96    #[track_caller]
97    pub fn unwrap(self) -> CompletedMarker {
98        match self {
99            Absent => {
100                panic!("Called `unwrap` on an `Absent` syntax");
101            }
102            Present(marker) => marker,
103        }
104    }
105
106    /// Returns the contained [ParsedSyntax::Present] value or passed default
107    #[allow(unused)]
108    #[inline]
109    pub fn unwrap_or(self, default: CompletedMarker) -> CompletedMarker {
110        match self {
111            Absent => default,
112            Present(marker) => marker,
113        }
114    }
115
116    /// Returns the contained [ParsedSyntax::Present] value or computes it from a clojure.
117    #[inline]
118    #[allow(unused)]
119    pub fn unwrap_or_else<F>(self, default: F) -> CompletedMarker
120    where
121        F: FnOnce() -> CompletedMarker,
122    {
123        match self {
124            Absent => default(),
125            Present(marker) => marker,
126        }
127    }
128
129    /// Returns the contained [ParsedSyntax::Present] value, consuming the self value.
130    ///
131    /// # Panics
132    ///
133    /// Panics if the value is an [ParsedSyntax::Absent] with a custom panic message provided by msg.
134    #[inline]
135    #[track_caller]
136    pub fn expect(self, msg: &str) -> CompletedMarker {
137        match self {
138            Present(marker) => marker,
139            Absent => panic!("{}", msg),
140        }
141    }
142
143    /// Maps a [ParsedSyntax::Present] `ParsedSyntax` by applying a function to a contained [ParsedSyntax::Present] value,
144    /// leaving an [ParsedSyntax::Absent] value untouched.
145    ///
146    /// This function can be used to compose the results of two functions.
147    pub fn map<F>(self, mapper: F) -> ParsedSyntax
148    where
149        F: FnOnce(CompletedMarker) -> CompletedMarker,
150    {
151        match self {
152            Absent => Absent,
153            Present(element) => Present(mapper(element)),
154        }
155    }
156
157    /// Returns the kind of the syntax if it is present or [None] otherwise
158    #[inline]
159    pub fn kind<P>(&self, p: &P) -> Option<P::Kind>
160    where
161        P: Parser,
162    {
163        match self {
164            Absent => None,
165            Present(marker) => Some(marker.kind(p)),
166        }
167    }
168
169    /// Adds a diagnostic at the current parser position if the syntax is present and return its marker.
170    #[inline]
171    pub fn add_diagnostic_if_present<P, E, D>(
172        self,
173        p: &mut P,
174        error_builder: E,
175    ) -> Option<CompletedMarker>
176    where
177        P: Parser,
178        E: FnOnce(&P, TextRange) -> D,
179        D: ToDiagnostic<P>,
180    {
181        match self {
182            Present(syntax) => {
183                let range = syntax.range(p);
184                let range = TextRange::new(range.start(), range.end());
185                let diagnostic = error_builder(p, range);
186                p.error(diagnostic);
187                Some(syntax)
188            }
189            Absent => None,
190        }
191    }
192
193    /// It returns the syntax if present or adds a diagnostic at the current parser position.
194    #[inline]
195    pub fn or_add_diagnostic<P, E, D>(self, p: &mut P, error_builder: E) -> Option<CompletedMarker>
196    where
197        P: Parser,
198        E: FnOnce(&P, TextRange) -> D,
199        D: ToDiagnostic<P>,
200    {
201        match self {
202            Present(syntax) => Some(syntax),
203            Absent => {
204                let diagnostic = error_builder(p, p.cur_range());
205                p.error(diagnostic);
206                None
207            }
208        }
209    }
210
211    /// It creates and returns a marker preceding this parsed syntax if it is present or starts
212    /// a new marker and adds an error to the current parser position.
213    /// See [CompletedMarker.precede]
214    #[inline]
215    pub fn precede_or_add_diagnostic<P, E, D>(self, p: &mut P, error_builder: E) -> Marker
216    where
217        P: Parser,
218        E: FnOnce(&P, TextRange) -> D,
219        D: ToDiagnostic<P>,
220    {
221        match self {
222            Present(completed) => completed.precede(p),
223            Absent => {
224                let diagnostic = error_builder(p, p.cur_range());
225                p.error(diagnostic);
226                p.start()
227            }
228        }
229    }
230
231    /// Creates a new marker that precedes this syntax or starts a new marker
232    #[inline]
233    pub fn precede<P>(self, p: &mut P) -> Marker
234    where
235        P: Parser,
236    {
237        match self {
238            Present(marker) => marker.precede(p),
239            Absent => p.start(),
240        }
241    }
242
243    /// Returns this Syntax if it is present in the source text or tries to recover the
244    /// parser if the syntax is absent. The recovery...
245    ///
246    /// * eats all unexpected tokens into a `Bogus*` node until the parser reaches one
247    ///   of the "safe tokens" configured in the [ParseRecoveryTokenSet].
248    /// * creates an error using the passed in error builder and adds it to the parsing diagnostics.
249    ///
250    /// The error recovery can fail if the parser is located at the EOF token or if the parser
251    /// is already at a valid position according to the [ParseRecoveryTokenSet].
252    pub fn or_recover_with_token_set<P, E>(
253        self,
254        p: &mut P,
255        recovery: &ParseRecoveryTokenSet<P::Kind>,
256        error_builder: E,
257    ) -> RecoveryResult
258    where
259        P: Parser,
260        E: FnOnce(&P, TextRange) -> ParseDiagnostic,
261    {
262        match self {
263            Present(syntax) => Ok(syntax),
264            Absent => match recovery.recover(p) {
265                Ok(recovered) => {
266                    let diagnostic = error_builder(p, recovered.range(p));
267                    p.error(diagnostic);
268                    Ok(recovered)
269                }
270
271                Err(recovery_error) => {
272                    let diagnostic = error_builder(p, p.cur_range());
273                    p.error(diagnostic);
274                    Err(recovery_error)
275                }
276            },
277        }
278    }
279
280    /// Returns this Syntax if it is present in the source text or tries to recover the
281    /// parser if the syntax is absent. The recovery...
282    ///
283    /// * eats all unexpected tokens into a `Bogus*` node until the parser reaches one
284    ///   of the "safe tokens" configured in the [ParseRecovery].
285    /// * creates an error using the passed in error builder and adds it to the parsing diagnostics.
286    ///
287    /// The error recovery can fail if the parser is located at the EOF token or if the parser
288    /// is already at a valid position according to the [ParseRecovery].
289    pub fn or_recover<'source, P, E, R>(
290        self,
291        p: &mut P,
292        recovery: &R,
293        error_builder: E,
294    ) -> RecoveryResult
295    where
296        P: Parser,
297        R: ParseRecovery<Parser<'source> = P>,
298        E: FnOnce(&P, TextRange) -> ParseDiagnostic,
299    {
300        match self {
301            Present(syntax) => Ok(syntax),
302            Absent => match recovery.recover(p) {
303                Ok(recovered) => {
304                    let diagnostic = error_builder(p, recovered.range(p));
305                    p.error(diagnostic);
306                    Ok(recovered)
307                }
308
309                Err(recovery_error) => {
310                    let diagnostic = error_builder(p, p.cur_range());
311                    p.error(diagnostic);
312                    Err(recovery_error)
313                }
314            },
315        }
316    }
317}
318
319impl From<CompletedMarker> for ParsedSyntax {
320    fn from(marker: CompletedMarker) -> Self {
321        Present(marker)
322    }
323}
324
325impl From<Option<CompletedMarker>> for ParsedSyntax {
326    fn from(option: Option<CompletedMarker>) -> Self {
327        match option {
328            Some(completed) => Present(completed),
329            None => Absent,
330        }
331    }
332}