resast/spanned/
mod.rs

1use std::borrow::Cow;
2
3pub mod decl;
4pub mod expr;
5pub mod pat;
6pub mod stmt;
7
8use decl::Decl;
9use expr::{Expr, Lit, Prop};
10use pat::Pat;
11use stmt::Stmt;
12
13use self::pat::RestPat;
14
15pub trait Node {
16    fn loc(&self) -> SourceLocation;
17}
18
19#[derive(Debug, Clone, PartialEq)]
20pub struct Ident<'a> {
21    pub slice: Slice<'a>,
22}
23
24impl<'a> Node for Ident<'a> {
25    fn loc(&self) -> SourceLocation {
26        self.slice.loc
27    }
28}
29
30impl<'a> From<Ident<'a>> for crate::Ident<'a> {
31    fn from(other: Ident<'a>) -> Self {
32        Self {
33            name: other.slice.source,
34        }
35    }
36}
37
38impl<'a> From<Slice<'a>> for Ident<'a> {
39    fn from(slice: Slice<'a>) -> Self {
40        Self { slice }
41    }
42}
43
44impl<'a> Ident<'a> {
45    pub fn name(&self) -> Cow<'a, str> {
46        self.slice.source.clone()
47    }
48}
49
50/// A fully parsed javascript program.
51///
52/// It is essentially a collection of `ProgramPart`s
53/// with a flag denoting if the representation is
54/// a ES6 Mod or a Script.
55#[derive(Debug, Clone, PartialEq)]
56pub enum Program<'a> {
57    /// An ES6 Mod
58    Mod(Vec<ProgramPart<'a>>),
59    /// Not an ES6 Mod
60    Script(Vec<ProgramPart<'a>>),
61}
62
63impl<'a> From<Program<'a>> for crate::Program<'a> {
64    fn from(other: Program<'a>) -> Self {
65        match other {
66            Program::Mod(inner) => Self::Mod(inner.into_iter().map(From::from).collect()),
67            Program::Script(inner) => Self::Script(inner.into_iter().map(From::from).collect()),
68        }
69    }
70}
71
72impl<'a> Node for Program<'a> {
73    fn loc(&self) -> SourceLocation {
74        match self {
75            Self::Mod(inner) => inner.loc(),
76            Self::Script(inner) => inner.loc(),
77        }
78    }
79}
80
81impl<'a> Program<'a> {
82    pub fn module(parts: Vec<ProgramPart<'a>>) -> Self {
83        Program::Mod(parts)
84    }
85    pub fn script(parts: Vec<ProgramPart<'a>>) -> Self {
86        Program::Script(parts)
87    }
88}
89
90impl<'a> Node for Vec<ProgramPart<'a>> {
91    fn loc(&self) -> SourceLocation {
92        let start = self
93            .first()
94            .map(|p| p.loc())
95            .unwrap_or_else(SourceLocation::zero);
96        let end = self.last().map(|p| p.loc()).unwrap_or(start);
97        SourceLocation {
98            start: start.start,
99            end: end.end,
100        }
101    }
102}
103
104/// A single part of a Javascript program.
105/// This will be either a Directive, Decl or a Stmt
106#[derive(Debug, Clone, PartialEq)]
107pub enum ProgramPart<'a> {
108    /// A Directive like `'use strict';`
109    Dir(Dir<'a>),
110    /// A variable, function or module declaration
111    Decl(Decl<'a>),
112    /// Any other kind of statement
113    Stmt(Stmt<'a>),
114}
115
116impl<'a> From<ProgramPart<'a>> for crate::ProgramPart<'a> {
117    fn from(other: ProgramPart<'a>) -> Self {
118        match other {
119            ProgramPart::Dir(inner) => Self::Dir(inner.into()),
120            ProgramPart::Decl(inner) => Self::Decl(inner.into()),
121            ProgramPart::Stmt(inner) => Self::Stmt(inner.into()),
122        }
123    }
124}
125
126impl<'a> Node for ProgramPart<'a> {
127    fn loc(&self) -> SourceLocation {
128        match self {
129            Self::Dir(inner) => inner.loc(),
130            Self::Decl(inner) => inner.loc(),
131            Self::Stmt(inner) => inner.loc(),
132        }
133    }
134}
135
136impl<'a> ProgramPart<'a> {
137    pub fn decl(inner: Decl<'a>) -> Self {
138        ProgramPart::Decl(inner)
139    }
140    pub fn stmt(inner: Stmt<'a>) -> Self {
141        ProgramPart::Stmt(inner)
142    }
143}
144
145/// pretty much always `'use strict'`, this can appear at the
146/// top of a file or function
147#[derive(Debug, Clone, PartialEq)]
148pub struct Dir<'a> {
149    pub expr: Lit<'a>,
150    pub dir: Cow<'a, str>,
151    pub semi_colon: Option<Slice<'a>>,
152}
153
154impl<'a> From<Dir<'a>> for crate::Dir<'a> {
155    fn from(other: Dir<'a>) -> Self {
156        Self {
157            expr: other.expr.into(),
158            dir: other.dir,
159        }
160    }
161}
162
163impl<'a> Node for Dir<'a> {
164    fn loc(&self) -> SourceLocation {
165        self.expr.loc()
166    }
167}
168
169/// A function, this will be part of either a function
170/// declaration (ID is required) or a function expression
171/// (ID is optional)
172/// ```js
173/// //function declaration
174/// function thing() {}
175/// //function expressions
176/// var x = function() {}
177/// let y = function q() {}
178/// ```
179#[derive(PartialEq, Debug, Clone)]
180pub struct Func<'a> {
181    pub keyword: Slice<'a>,
182    pub id: Option<Ident<'a>>,
183    pub open_paren: Slice<'a>,
184    pub params: Vec<ListEntry<'a, FuncArg<'a>>>,
185    pub close_paren: Slice<'a>,
186    pub body: FuncBody<'a>,
187    pub star: Option<Slice<'a>>,
188    pub keyword_async: Option<Slice<'a>>,
189}
190
191impl<'a> Func<'a> {
192    pub fn is_async(&self) -> bool {
193        self.keyword_async.is_some()
194    }
195    pub fn generator(&self) -> bool {
196        self.star.is_some()
197    }
198}
199
200impl<'a> From<Func<'a>> for crate::Func<'a> {
201    fn from(other: Func<'a>) -> Self {
202        Self {
203            generator: other.generator(),
204            is_async: other.is_async(),
205            id: other.id.map(From::from),
206            params: other
207                .params
208                .into_iter()
209                .map(|e| From::from(e.item))
210                .collect(),
211            body: other.body.into(),
212        }
213    }
214}
215
216impl<'a> Node for Func<'a> {
217    fn loc(&self) -> SourceLocation {
218        let start = if let Some(keyword) = &self.keyword_async {
219            keyword.loc.start
220        } else {
221            self.keyword.loc.start
222        };
223        let end = self.body.close_brace.loc.end.clone();
224        SourceLocation { start, end }
225    }
226}
227
228#[derive(Debug, Clone, PartialEq)]
229pub struct FuncArgEntry<'a> {
230    pub value: FuncArg<'a>,
231    pub comma: Option<Slice<'a>>,
232}
233
234impl<'a> From<FuncArgEntry<'a>> for crate::FuncArg<'a> {
235    fn from(other: FuncArgEntry<'a>) -> Self {
236        other.value.into()
237    }
238}
239
240impl<'a> Node for FuncArgEntry<'a> {
241    fn loc(&self) -> SourceLocation {
242        if let Some(comma) = &self.comma {
243            return SourceLocation {
244                start: self.value.loc().start,
245                end: comma.loc.end,
246            };
247        }
248        self.value.loc()
249    }
250}
251
252/// A single function argument from a function signature
253#[derive(Debug, Clone, PartialEq)]
254pub enum FuncArg<'a> {
255    Expr(Expr<'a>),
256    Pat(Pat<'a>),
257    Rest(Box<RestPat<'a>>),
258}
259
260impl<'a> From<FuncArg<'a>> for crate::FuncArg<'a> {
261    fn from(other: FuncArg<'a>) -> Self {
262        match other {
263            FuncArg::Expr(inner) => Self::Expr(inner.into()),
264            FuncArg::Pat(inner) => Self::Pat(inner.into()),
265            FuncArg::Rest(inner) => {
266                Self::Pat(crate::pat::Pat::RestElement(Box::new(inner.pat.into())))
267            }
268        }
269    }
270}
271
272impl<'a> Node for FuncArg<'a> {
273    fn loc(&self) -> SourceLocation {
274        match self {
275            FuncArg::Expr(inner) => inner.loc(),
276            FuncArg::Pat(inner) => inner.loc(),
277            FuncArg::Rest(inner) => inner.loc(),
278        }
279    }
280}
281
282/// The block statement that makes up the function's body
283#[derive(Debug, Clone, PartialEq)]
284pub struct FuncBody<'a> {
285    pub open_brace: Slice<'a>,
286    pub stmts: Vec<ProgramPart<'a>>,
287    pub close_brace: Slice<'a>,
288}
289
290impl<'a> From<FuncBody<'a>> for crate::FuncBody<'a> {
291    fn from(other: FuncBody<'a>) -> Self {
292        Self(other.stmts.into_iter().map(From::from).collect())
293    }
294}
295
296impl<'a> Node for FuncBody<'a> {
297    fn loc(&self) -> SourceLocation {
298        SourceLocation {
299            start: self.open_brace.loc.start,
300            end: self.close_brace.loc.end,
301        }
302    }
303}
304
305/// A way to declare object templates
306/// ```js
307/// class Thing {
308///     constructor() {
309///         this._a = 0;
310///     }
311///     stuff() {
312///         return 'stuff'
313///     }
314///     set a(value) {
315///         if (value > 100) {
316///             this._a = 0;
317///         } else {
318///             this._a = value;
319///         }
320///     }
321///     get a() {
322///         return this._a;
323///     }
324/// }
325/// let y = class {
326///     constructor() {
327///         this.a = 100;
328///     }
329/// }
330/// ```
331#[derive(PartialEq, Debug, Clone)]
332pub struct Class<'a> {
333    pub keyword: Slice<'a>,
334    pub id: Option<Ident<'a>>,
335    pub super_class: Option<SuperClass<'a>>,
336    pub body: ClassBody<'a>,
337}
338
339impl<'a> From<Class<'a>> for crate::Class<'a> {
340    fn from(other: Class<'a>) -> Self {
341        Self {
342            id: other.id.map(From::from),
343            super_class: other.super_class.map(|e| Box::new(From::from(e.expr))),
344            body: other.body.into(),
345        }
346    }
347}
348
349impl<'a> Node for Class<'a> {
350    fn loc(&self) -> SourceLocation {
351        SourceLocation {
352            start: self.keyword.loc.start,
353            end: self.body.close_brace.loc.end,
354        }
355    }
356}
357
358#[derive(PartialEq, Debug, Clone)]
359pub struct SuperClass<'a> {
360    pub keyword_extends: Slice<'a>,
361    pub expr: Expr<'a>,
362}
363
364#[derive(Debug, Clone, PartialEq)]
365pub struct ClassBody<'a> {
366    pub open_brace: Slice<'a>,
367    pub props: Vec<Prop<'a>>,
368    pub close_brace: Slice<'a>,
369}
370
371impl<'a> From<ClassBody<'a>> for crate::ClassBody<'a> {
372    fn from(other: ClassBody<'a>) -> Self {
373        Self(other.props.into_iter().map(From::from).collect())
374    }
375}
376
377impl<'a> Node for ClassBody<'a> {
378    fn loc(&self) -> SourceLocation {
379        let start = self.open_brace.loc.start;
380        let end = self.close_brace.loc.end;
381        SourceLocation { start, end }
382    }
383}
384
385/// The kind of variable being defined (`var`/`let`/`const`)
386#[derive(Debug, Clone, PartialEq)]
387pub enum VarKind<'a> {
388    Var(Option<Slice<'a>>),
389    Let(Slice<'a>),
390    Const(Slice<'a>),
391}
392
393impl<'a> From<VarKind<'a>> for crate::VarKind {
394    fn from(other: VarKind<'a>) -> Self {
395        match other {
396            VarKind::Var(_) => Self::Var,
397            VarKind::Let(_) => Self::Let,
398            VarKind::Const(_) => Self::Const,
399        }
400    }
401}
402
403impl<'a> Node for VarKind<'a> {
404    fn loc(&self) -> SourceLocation {
405        match self {
406            VarKind::Var(Some(slice)) => slice.loc,
407            VarKind::Let(slice) => slice.loc,
408            VarKind::Const(slice) => slice.loc,
409            _ => SourceLocation::zero(),
410        }
411    }
412}
413
414impl<'a> VarKind<'a> {
415    pub fn is_var(&self) -> bool {
416        matches!(self, VarKind::Var(_))
417    }
418}
419
420/// The available operators for assignment Exprs
421#[derive(Debug, Clone, PartialEq)]
422pub enum AssignOp<'a> {
423    Equal(Slice<'a>),
424    PlusEqual(Slice<'a>),
425    MinusEqual(Slice<'a>),
426    TimesEqual(Slice<'a>),
427    DivEqual(Slice<'a>),
428    ModEqual(Slice<'a>),
429    LeftShiftEqual(Slice<'a>),
430    RightShiftEqual(Slice<'a>),
431    UnsignedRightShiftEqual(Slice<'a>),
432    OrEqual(Slice<'a>),
433    XOrEqual(Slice<'a>),
434    AndEqual(Slice<'a>),
435    PowerOfEqual(Slice<'a>),
436}
437
438impl<'a> From<AssignOp<'a>> for crate::AssignOp {
439    fn from(other: AssignOp<'a>) -> Self {
440        match other {
441            AssignOp::Equal(_) => Self::Equal,
442            AssignOp::PlusEqual(_) => Self::PlusEqual,
443            AssignOp::MinusEqual(_) => Self::MinusEqual,
444            AssignOp::TimesEqual(_) => Self::TimesEqual,
445            AssignOp::DivEqual(_) => Self::DivEqual,
446            AssignOp::ModEqual(_) => Self::ModEqual,
447            AssignOp::LeftShiftEqual(_) => Self::LeftShiftEqual,
448            AssignOp::RightShiftEqual(_) => Self::RightShiftEqual,
449            AssignOp::UnsignedRightShiftEqual(_) => Self::UnsignedRightShiftEqual,
450            AssignOp::OrEqual(_) => Self::OrEqual,
451            AssignOp::XOrEqual(_) => Self::XOrEqual,
452            AssignOp::AndEqual(_) => Self::AndEqual,
453            AssignOp::PowerOfEqual(_) => Self::PowerOfEqual,
454        }
455    }
456}
457
458impl<'a> Node for AssignOp<'a> {
459    fn loc(&self) -> SourceLocation {
460        match self {
461            AssignOp::Equal(slice) => slice.loc,
462            AssignOp::PlusEqual(slice) => slice.loc,
463            AssignOp::MinusEqual(slice) => slice.loc,
464            AssignOp::TimesEqual(slice) => slice.loc,
465            AssignOp::DivEqual(slice) => slice.loc,
466            AssignOp::ModEqual(slice) => slice.loc,
467            AssignOp::LeftShiftEqual(slice) => slice.loc,
468            AssignOp::RightShiftEqual(slice) => slice.loc,
469            AssignOp::UnsignedRightShiftEqual(slice) => slice.loc,
470            AssignOp::OrEqual(slice) => slice.loc,
471            AssignOp::XOrEqual(slice) => slice.loc,
472            AssignOp::AndEqual(slice) => slice.loc,
473            AssignOp::PowerOfEqual(slice) => slice.loc,
474        }
475    }
476}
477
478/// The available logical operators
479#[derive(Debug, Clone, PartialEq)]
480pub enum LogicalOp<'a> {
481    Or(Slice<'a>),
482    And(Slice<'a>),
483}
484
485impl<'a> From<LogicalOp<'a>> for crate::LogicalOp {
486    fn from(other: LogicalOp<'a>) -> Self {
487        match other {
488            LogicalOp::Or(_) => Self::Or,
489            LogicalOp::And(_) => Self::And,
490        }
491    }
492}
493
494impl<'a> Node for LogicalOp<'a> {
495    fn loc(&self) -> SourceLocation {
496        match self {
497            LogicalOp::Or(slice) => slice.loc,
498            LogicalOp::And(slice) => slice.loc,
499        }
500    }
501}
502
503/// The available operations for `Binary` Exprs
504#[derive(Debug, Clone, PartialEq)]
505pub enum BinaryOp<'a> {
506    Equal(Slice<'a>),
507    NotEqual(Slice<'a>),
508    StrictEqual(Slice<'a>),
509    StrictNotEqual(Slice<'a>),
510    LessThan(Slice<'a>),
511    GreaterThan(Slice<'a>),
512    LessThanEqual(Slice<'a>),
513    GreaterThanEqual(Slice<'a>),
514    LeftShift(Slice<'a>),
515    RightShift(Slice<'a>),
516    UnsignedRightShift(Slice<'a>),
517    Plus(Slice<'a>),
518    Minus(Slice<'a>),
519    Times(Slice<'a>),
520    Over(Slice<'a>),
521    Mod(Slice<'a>),
522    Or(Slice<'a>),
523    XOr(Slice<'a>),
524    And(Slice<'a>),
525    In(Slice<'a>),
526    InstanceOf(Slice<'a>),
527    PowerOf(Slice<'a>),
528}
529
530impl<'a> From<BinaryOp<'a>> for crate::BinaryOp {
531    fn from(other: BinaryOp<'a>) -> Self {
532        match other {
533            BinaryOp::Equal(_) => Self::Equal,
534            BinaryOp::NotEqual(_) => Self::NotEqual,
535            BinaryOp::StrictEqual(_) => Self::StrictEqual,
536            BinaryOp::StrictNotEqual(_) => Self::StrictNotEqual,
537            BinaryOp::LessThan(_) => Self::LessThan,
538            BinaryOp::GreaterThan(_) => Self::GreaterThan,
539            BinaryOp::LessThanEqual(_) => Self::LessThanEqual,
540            BinaryOp::GreaterThanEqual(_) => Self::GreaterThanEqual,
541            BinaryOp::LeftShift(_) => Self::LeftShift,
542            BinaryOp::RightShift(_) => Self::RightShift,
543            BinaryOp::UnsignedRightShift(_) => Self::UnsignedRightShift,
544            BinaryOp::Plus(_) => Self::Plus,
545            BinaryOp::Minus(_) => Self::Minus,
546            BinaryOp::Times(_) => Self::Times,
547            BinaryOp::Over(_) => Self::Over,
548            BinaryOp::Mod(_) => Self::Mod,
549            BinaryOp::Or(_) => Self::Or,
550            BinaryOp::XOr(_) => Self::XOr,
551            BinaryOp::And(_) => Self::And,
552            BinaryOp::In(_) => Self::In,
553            BinaryOp::InstanceOf(_) => Self::InstanceOf,
554            BinaryOp::PowerOf(_) => Self::PowerOf,
555        }
556    }
557}
558
559impl<'a> Node for BinaryOp<'a> {
560    fn loc(&self) -> SourceLocation {
561        match self {
562            BinaryOp::Equal(slice) => slice.loc,
563            BinaryOp::NotEqual(slice) => slice.loc,
564            BinaryOp::StrictEqual(slice) => slice.loc,
565            BinaryOp::StrictNotEqual(slice) => slice.loc,
566            BinaryOp::LessThan(slice) => slice.loc,
567            BinaryOp::GreaterThan(slice) => slice.loc,
568            BinaryOp::LessThanEqual(slice) => slice.loc,
569            BinaryOp::GreaterThanEqual(slice) => slice.loc,
570            BinaryOp::LeftShift(slice) => slice.loc,
571            BinaryOp::RightShift(slice) => slice.loc,
572            BinaryOp::UnsignedRightShift(slice) => slice.loc,
573            BinaryOp::Plus(slice) => slice.loc,
574            BinaryOp::Minus(slice) => slice.loc,
575            BinaryOp::Times(slice) => slice.loc,
576            BinaryOp::Over(slice) => slice.loc,
577            BinaryOp::Mod(slice) => slice.loc,
578            BinaryOp::Or(slice) => slice.loc,
579            BinaryOp::XOr(slice) => slice.loc,
580            BinaryOp::And(slice) => slice.loc,
581            BinaryOp::In(slice) => slice.loc,
582            BinaryOp::InstanceOf(slice) => slice.loc,
583            BinaryOp::PowerOf(slice) => slice.loc,
584        }
585    }
586}
587
588/// `++` or `--`
589#[derive(Debug, Clone, PartialEq)]
590pub enum UpdateOp<'a> {
591    Increment(Slice<'a>),
592    Decrement(Slice<'a>),
593}
594
595impl<'a> From<UpdateOp<'a>> for crate::UpdateOp {
596    fn from(other: UpdateOp<'a>) -> Self {
597        match other {
598            UpdateOp::Increment(_) => Self::Increment,
599            UpdateOp::Decrement(_) => Self::Decrement,
600        }
601    }
602}
603
604impl<'a> Node for UpdateOp<'a> {
605    fn loc(&self) -> SourceLocation {
606        match self {
607            UpdateOp::Increment(slice) => slice.loc,
608            UpdateOp::Decrement(slice) => slice.loc,
609        }
610    }
611}
612
613/// The allowed operators for an Expr
614/// to be `Unary`
615#[derive(Debug, Clone, PartialEq)]
616pub enum UnaryOp<'a> {
617    Minus(Slice<'a>),
618    Plus(Slice<'a>),
619    Not(Slice<'a>),
620    Tilde(Slice<'a>),
621    TypeOf(Slice<'a>),
622    Void(Slice<'a>),
623    Delete(Slice<'a>),
624}
625
626impl<'a> From<UnaryOp<'a>> for crate::UnaryOp {
627    fn from(other: UnaryOp<'a>) -> Self {
628        match other {
629            UnaryOp::Minus(_) => Self::Minus,
630            UnaryOp::Plus(_) => Self::Plus,
631            UnaryOp::Not(_) => Self::Not,
632            UnaryOp::Tilde(_) => Self::Tilde,
633            UnaryOp::TypeOf(_) => Self::TypeOf,
634            UnaryOp::Void(_) => Self::Void,
635            UnaryOp::Delete(_) => Self::Delete,
636        }
637    }
638}
639
640impl<'a> Node for UnaryOp<'a> {
641    fn loc(&self) -> SourceLocation {
642        match self {
643            UnaryOp::Minus(slice) => slice.loc,
644            UnaryOp::Plus(slice) => slice.loc,
645            UnaryOp::Not(slice) => slice.loc,
646            UnaryOp::Tilde(slice) => slice.loc,
647            UnaryOp::TypeOf(slice) => slice.loc,
648            UnaryOp::Void(slice) => slice.loc,
649            UnaryOp::Delete(slice) => slice.loc,
650        }
651    }
652}
653
654#[derive(Debug, Clone, PartialEq)]
655pub struct Slice<'a> {
656    pub source: Cow<'a, str>,
657    pub loc: SourceLocation,
658}
659
660#[derive(Debug, Clone, PartialEq, Copy)]
661pub struct SourceLocation {
662    pub start: Position,
663    pub end: Position,
664}
665
666impl SourceLocation {
667    pub fn new(start_line: usize, start_column: usize, end_line: usize, end_column: usize) -> Self {
668        Self {
669            start: Position {
670                line: start_line,
671                column: start_column,
672            },
673            end: Position {
674                line: end_line,
675                column: end_column,
676            },
677        }
678    }
679    fn zero() -> Self {
680        Self::new(0, 0, 0, 0)
681    }
682}
683
684impl core::cmp::PartialOrd for SourceLocation {
685    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
686        match self.start.partial_cmp(&other.start) {
687            Some(core::cmp::Ordering::Equal) => {}
688            ord => return ord,
689        }
690        self.end.partial_cmp(&other.end)
691    }
692}
693
694#[derive(Debug, Clone, PartialEq, Copy)]
695pub struct Position {
696    pub line: usize,
697    pub column: usize,
698}
699
700impl std::cmp::PartialOrd for Position {
701    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
702        let line = self.line.partial_cmp(&other.line)?;
703        if matches!(line, core::cmp::Ordering::Equal) {
704            return self.column.partial_cmp(&other.column);
705        }
706        Some(line)
707    }
708}
709
710impl std::ops::Add for Position {
711    type Output = Self;
712
713    fn add(self, rhs: Self) -> Self::Output {
714        Self {
715            line: self.line + rhs.line,
716            column: self.column + rhs.column,
717        }
718    }
719}
720
721impl std::ops::Sub for Position {
722    type Output = Self;
723
724    fn sub(self, rhs: Self) -> Self::Output {
725        Self {
726            line: self.line - rhs.line,
727            column: self.column - rhs.column,
728        }
729    }
730}
731
732#[derive(Debug, Clone, PartialEq)]
733pub struct ListEntry<'a, T> {
734    pub item: T,
735    pub comma: Option<Slice<'a>>,
736}
737
738impl<'a, T> ListEntry<'a, T> {
739    pub fn no_comma(item: T) -> Self {
740        Self { item, comma: None }
741    }
742}
743
744impl<'a, T> Node for ListEntry<'a, T>
745where
746    T: Node,
747{
748    fn loc(&self) -> SourceLocation {
749        if let Some(comma) = &self.comma {
750            return SourceLocation {
751                start: self.item.loc().start,
752                end: comma.loc.end,
753            };
754        }
755        self.item.loc()
756    }
757}