garden_lang_parser/
ast.rs

1//! Syntax tree definitions for Garden.
2
3use rustc_hash::FxHashMap;
4
5use std::{
6    fmt::Display,
7    path::{Path, PathBuf},
8    rc::Rc,
9};
10
11use crate::position::Position;
12
13#[derive(Clone, PartialEq, Eq, Hash)]
14pub struct TypeName {
15    pub name: String,
16}
17
18impl TypeName {
19    pub fn is_no_value(&self) -> bool {
20        self.name == "NoValue"
21    }
22}
23
24impl std::fmt::Debug for TypeName {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        write!(f, "{}", self.name)
27    }
28}
29impl Display for TypeName {
30    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31        write!(f, "{}", self.name)
32    }
33}
34
35impl From<&str> for TypeName {
36    fn from(s: &str) -> Self {
37        TypeName { name: s.to_owned() }
38    }
39}
40
41impl From<&String> for TypeName {
42    fn from(s: &String) -> Self {
43        TypeName { name: s.to_owned() }
44    }
45}
46
47#[derive(Clone, Eq)]
48pub struct TypeSymbol {
49    pub name: TypeName,
50    pub position: Position,
51    pub id: SyntaxId,
52}
53
54/// Only consider the name when comparing type symbols. This is
55/// important when the runtime checks values have the same type.
56impl PartialEq for TypeSymbol {
57    fn eq(&self, other: &Self) -> bool {
58        self.name == other.name
59    }
60}
61
62impl std::fmt::Debug for TypeSymbol {
63    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64        if std::env::var("VERBOSE").is_ok() {
65            f.debug_struct("TypeSymbol")
66                .field("name", &self.name)
67                .field("position", &self.position)
68                .field("id", &self.id)
69                .finish()
70        } else {
71            write!(f, "TypeSymbol\"{}\"", self.name.name)
72        }
73    }
74}
75
76impl Display for TypeSymbol {
77    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78        write!(f, "{}", self.name)
79    }
80}
81
82impl TypeSymbol {
83    pub fn is_placeholder(&self) -> bool {
84        // TODO: Prevent users from writing this symbol in userland code.
85        self.name.name == "__placeholder" || self.name.name == "__reserved_word_placeholder"
86    }
87}
88
89/// Represents a type name in source code. This might be a concrete
90/// type, such as `List<Int>`, or may refer to generics
91/// e.g. `List<T>`.
92#[derive(Clone, Debug, PartialEq, Eq)]
93pub struct TypeHint {
94    pub sym: TypeSymbol,
95    pub args: Vec<TypeHint>,
96    pub position: Position,
97}
98
99impl TypeHint {
100    /// The source code equivalent of this type hint.
101    pub fn as_src(&self) -> String {
102        if self.args.is_empty() {
103            format!("{}", self.sym.name)
104        } else if self.sym.name.name == "Tuple" {
105            let formatted_args = self
106                .args
107                .iter()
108                .map(|a| a.as_src())
109                .collect::<Vec<_>>()
110                .join(", ");
111
112            format!("({})", formatted_args)
113        } else {
114            let formatted_args = self
115                .args
116                .iter()
117                .map(|a| a.as_src())
118                .collect::<Vec<_>>()
119                .join(", ");
120            format!("{}<{}>", self.sym.name, formatted_args)
121        }
122    }
123}
124
125#[derive(Clone, Debug, PartialEq, Eq, Hash)]
126pub struct SymbolName {
127    pub name: String,
128}
129
130impl Display for SymbolName {
131    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132        write!(f, "{}", self.name)
133    }
134}
135
136impl SymbolName {
137    pub fn is_underscore(&self) -> bool {
138        self.name == "_"
139    }
140
141    pub fn is_placeholder(&self) -> bool {
142        // TODO: Prevent users from writing this symbol in userland code.
143        self.name == "__placeholder" || self.name == "__reserved_word_placeholder"
144    }
145}
146
147impl From<&str> for SymbolName {
148    fn from(s: &str) -> Self {
149        SymbolName { name: s.to_owned() }
150    }
151}
152
153#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
154pub struct InternedSymbolId(pub usize);
155
156/// A symbol representing a value, such as a local variable, a
157/// function name or a method name.
158///
159/// See also [`TypeSymbol`].
160#[derive(Clone, PartialEq, Eq)]
161pub struct Symbol {
162    pub position: Position,
163    pub name: SymbolName,
164    pub id: SyntaxId,
165    pub interned_id: InternedSymbolId,
166}
167
168impl Symbol {
169    pub fn new<S: AsRef<str>>(position: Position, name: S, id_gen: &mut IdGenerator) -> Self {
170        let name = SymbolName {
171            name: name.as_ref().to_owned(),
172        };
173
174        Self {
175            interned_id: id_gen.intern_symbol(&name),
176            position,
177            name,
178            id: id_gen.next(),
179        }
180    }
181
182    pub fn is_placeholder(&self) -> bool {
183        self.name.is_placeholder()
184    }
185}
186
187impl std::fmt::Debug for Symbol {
188    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
189        if std::env::var("VERBOSE").is_ok() {
190            f.debug_struct("Symbol")
191                .field("name", &self.name)
192                .field("position", &self.position)
193                .field("id", &self.id)
194                .field("interned_id", &self.interned_id)
195                .finish()
196        } else {
197            write!(f, "Symbol\"{}\"", self.name.name)
198        }
199    }
200}
201
202#[derive(Clone, Debug, PartialEq, Eq)]
203pub struct SymbolWithHint {
204    pub symbol: Symbol,
205    pub hint: Option<TypeHint>,
206}
207
208#[derive(Debug, Clone, Copy, PartialEq, Eq)]
209pub enum BinaryOperatorKind {
210    Add,
211    Subtract,
212    Multiply,
213    Divide,
214    Modulo,
215    Exponent,
216    Equal,
217    NotEqual,
218    LessThan,
219    LessThanOrEqual,
220    GreaterThan,
221    GreaterThanOrEqual,
222    And,
223    Or,
224    StringConcat,
225}
226
227impl Display for BinaryOperatorKind {
228    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
229        let s = match self {
230            BinaryOperatorKind::Add => "+",
231            BinaryOperatorKind::Subtract => "-",
232            BinaryOperatorKind::Multiply => "*",
233            BinaryOperatorKind::Divide => "/",
234            BinaryOperatorKind::Modulo => "%",
235            BinaryOperatorKind::Exponent => "^",
236            BinaryOperatorKind::Equal => "==",
237            BinaryOperatorKind::NotEqual => "!=",
238            BinaryOperatorKind::LessThan => "<",
239            BinaryOperatorKind::LessThanOrEqual => "<=",
240            BinaryOperatorKind::GreaterThan => ">",
241            BinaryOperatorKind::GreaterThanOrEqual => ">=",
242            BinaryOperatorKind::And => "&&",
243            BinaryOperatorKind::Or => "||",
244            BinaryOperatorKind::StringConcat => "^",
245        };
246        write!(f, "{}", s)
247    }
248}
249
250#[derive(Debug, Clone, Copy, PartialEq, Eq)]
251pub enum AssignUpdateKind {
252    Add,
253    Subtract,
254}
255
256impl AssignUpdateKind {
257    pub fn as_src(&self) -> &'static str {
258        match self {
259            AssignUpdateKind::Add => "+=",
260            AssignUpdateKind::Subtract => "-=",
261        }
262    }
263}
264
265/// The left hand side of a case in a `match`
266/// expression. E.g. `Foo(bar)` in the following code.
267///
268/// ```garden
269/// match x {
270///   Foo(bar) => {}
271/// }
272/// ```
273#[derive(Debug, Clone, PartialEq, Eq)]
274pub struct Pattern {
275    /// E.g. `Some` or `None`.
276    pub variant_sym: Symbol,
277    /// E.g. `foo` in `Some(foo) => `.
278    pub payload: Option<LetDestination>,
279}
280
281#[derive(Debug, Clone, PartialEq, Eq)]
282pub struct ExpressionWithComma {
283    pub expr: Rc<Expression>,
284    pub comma: Option<Position>,
285}
286
287#[derive(Debug, Clone, PartialEq, Eq)]
288pub struct ParenthesizedExpression {
289    pub open_paren: Position,
290    pub expr: Rc<Expression>,
291    pub close_paren: Position,
292}
293
294#[derive(Debug, Clone, PartialEq, Eq)]
295pub struct ParenthesizedArguments {
296    pub open_paren: Position,
297    pub arguments: Vec<ExpressionWithComma>,
298    pub close_paren: Position,
299}
300
301#[derive(Debug, Clone, PartialEq, Eq)]
302pub enum LetDestination {
303    /// ```garden
304    /// let x = y
305    /// ```
306    Symbol(Symbol),
307    /// ```garden
308    /// let (x, y) = z
309    /// ```
310    Destructure(Vec<Symbol>),
311}
312
313#[derive(Debug, Clone, PartialEq, Eq)]
314pub enum Expression_ {
315    /// ```garden
316    /// match x {
317    ///     None => { get_value() }
318    ///     Some(y) => y + 1,
319    ///     _ => error("yikes"),
320    /// }
321    /// ```
322    Match(Rc<Expression>, Vec<(Pattern, Block)>),
323    /// ```garden
324    /// if x { y }
325    /// if x { y } else { z }
326    /// ```
327    If(Rc<Expression>, Block, Option<Block>),
328    /// ```garden
329    /// while x { y }
330    /// ```
331    While(Rc<Expression>, Block),
332    /// ```garden
333    /// for x in y { z }
334    /// for (foo, bar) in y { z }
335    /// ```
336    ForIn(LetDestination, Rc<Expression>, Block),
337    /// ```garden
338    /// break
339    /// ```
340    Break,
341    /// ```garden
342    /// continue
343    /// ```
344    Continue,
345    /// ```garden
346    /// x = y
347    /// ```
348    Assign(Symbol, Rc<Expression>),
349    /// ```garden
350    /// x += y
351    /// x -= y
352    /// ```
353    AssignUpdate(Symbol, AssignUpdateKind, Rc<Expression>),
354    /// ```garden
355    /// let x = y
356    /// let x: T = y
357    /// let (x, y) = z
358    /// ```
359    Let(LetDestination, Option<TypeHint>, Rc<Expression>),
360    /// ```garden
361    /// return x
362    /// return // equivalent to `return Unit`
363    /// ```
364    Return(Option<Rc<Expression>>),
365    /// ```garden
366    /// 123
367    /// ```
368    IntLiteral(i64),
369    /// ```garden
370    /// "foo"
371    /// ```
372    StringLiteral(String),
373    /// ```garden
374    /// [x, y]
375    /// ```
376    ListLiteral(Vec<ExpressionWithComma>),
377    /// ```garden
378    /// ()
379    /// (x,)
380    /// (x, y)
381    /// ```
382    TupleLiteral(Vec<Rc<Expression>>),
383    /// ```garden
384    /// Foo { x: 1, y: bar() }
385    /// ```
386    ///
387    /// Field values are executed in the order they occur in source
388    /// code, so we want an ordered data type here.
389    StructLiteral(TypeSymbol, Vec<(Symbol, Rc<Expression>)>),
390    /// ```garden
391    /// x + y
392    /// x < y
393    /// x && y
394    /// ```
395    BinaryOperator(Rc<Expression>, BinaryOperatorKind, Rc<Expression>),
396    /// ```garden
397    /// x
398    /// ```
399    Variable(Symbol),
400    /// ```garden
401    /// x()
402    /// ```
403    Call(Rc<Expression>, ParenthesizedArguments),
404    /// ```garden
405    /// foo.bar(x, y)
406    /// ```
407    MethodCall(Rc<Expression>, Symbol, ParenthesizedArguments),
408    /// ```garden
409    /// foo.bar
410    /// ```
411    DotAccess(Rc<Expression>, Symbol),
412    /// ```garden
413    /// fun(x, y) { x + y }
414    /// ```
415    FunLiteral(FunInfo),
416    /// ```garden
417    /// assert(x)
418    /// ```
419    Assert(Rc<Expression>),
420    /// Parentheses used for grouping, particularly in nested binary operators.
421    ///
422    /// ```garden
423    /// (x)
424    /// x * (y * z)
425    /// ```
426    Parentheses(Position, Rc<Expression>, Position),
427    /// We had a parse error in this position, so there's no
428    /// expression.
429    Invalid,
430}
431
432impl Expression_ {
433    pub(crate) fn is_invalid_or_placeholder(&self) -> bool {
434        match self {
435            Expression_::Variable(sym) => sym.is_placeholder(),
436            Expression_::Invalid => true,
437            _ => false,
438        }
439    }
440}
441
442/// A syntactic item that the IDE can interact with, such as an
443/// expression or a variable name.
444#[derive(Clone, PartialEq, Eq, Hash, Copy)]
445pub struct SyntaxId(pub usize);
446
447impl std::fmt::Debug for SyntaxId {
448    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
449        // Avoid deriving Debug because otherwise we get:
450        //
451        // SyntaxId(
452        //   123
453        // )
454        //
455        // which is too verbose.
456        write!(f, "SyntaxId({})", self.0)
457    }
458}
459
460/// An ID that represents a piece of syntax that we can perform
461/// operations on, such as go-to-def.
462#[derive(Debug, Clone, PartialEq, Eq, Hash)]
463pub enum AstId {
464    /// Syntax ID of an expression.
465    Expr(SyntaxId),
466    /// Syntax ID of a symbol. This is not necessarily part of an
467    /// expression: it could be a function parameter or type name.
468    ///
469    /// ```
470    /// fun f(foo: Int) {}
471    /// ```
472    Sym(SyntaxId),
473    /// Syntax ID of a type symbol.
474    ///
475    /// ```
476    /// fun f(_: Foo) {}
477    /// ```
478    TypeSym(SyntaxId),
479    /// An import item.
480    ///
481    /// ```
482    /// import "./foo.gdn"
483    /// ```
484    Import(SyntaxId),
485}
486
487impl AstId {
488    pub fn id(&self) -> SyntaxId {
489        match self {
490            AstId::Expr(syntax_id) => *syntax_id,
491            AstId::Sym(syntax_id) => *syntax_id,
492            AstId::TypeSym(syntax_id) => *syntax_id,
493            AstId::Import(syntax_id) => *syntax_id,
494        }
495    }
496}
497
498#[derive(Debug, Clone)]
499pub struct IdGenerator {
500    pub next_id: SyntaxId,
501    pub interned: FxHashMap<SymbolName, InternedSymbolId>,
502    pub intern_id_to_name: FxHashMap<InternedSymbolId, SymbolName>,
503}
504
505impl Default for IdGenerator {
506    fn default() -> Self {
507        Self {
508            next_id: SyntaxId(0),
509            interned: FxHashMap::default(),
510            intern_id_to_name: FxHashMap::default(),
511        }
512    }
513}
514
515impl IdGenerator {
516    #[allow(clippy::should_implement_trait)]
517    pub fn next(&mut self) -> SyntaxId {
518        let next_id = self.next_id;
519        self.next_id = SyntaxId(next_id.0 + 1);
520        next_id
521    }
522
523    pub fn intern_symbol(&mut self, name: &SymbolName) -> InternedSymbolId {
524        match self.interned.get(name) {
525            Some(id) => *id,
526            None => {
527                let id = InternedSymbolId(self.interned.len());
528                self.interned.insert(name.clone(), id);
529                self.intern_id_to_name.insert(id, name.clone());
530                id
531            }
532        }
533    }
534}
535
536/// Stores the source code of all the files we've loaded.
537#[derive(Debug, Clone, Default)]
538pub struct Vfs {
539    // TODO: support a single path having different strings, because
540    // users can re-evaluate individual functions after modification.
541    file_srcs: FxHashMap<PathBuf, String>,
542}
543
544impl Vfs {
545    pub fn insert(&mut self, path: PathBuf, src: String) {
546        self.file_srcs.insert(path, src);
547    }
548
549    pub fn file_src(&self, path: &Path) -> Option<&String> {
550        self.file_srcs.get(path)
551    }
552
553    pub fn pos_src(&self, pos: &Position) -> Option<&str> {
554        match self.file_srcs.get(pos.path.as_path()) {
555            Some(file) => Some(&file[pos.start_offset..pos.end_offset]),
556            None => None,
557        }
558    }
559}
560
561#[derive(Debug, Clone, PartialEq, Eq)]
562pub struct Expression {
563    pub position: Position,
564    pub expr_: Expression_,
565    /// Is this expression in a position where its value is used?
566    /// This is only false in blocks, e.g. in `{ foo() bar() }`,
567    /// `foo()` is ignored.
568    pub value_is_used: bool,
569    pub id: SyntaxId,
570}
571
572impl Expression {
573    pub(crate) fn new(position: Position, expr_: Expression_, id: SyntaxId) -> Self {
574        Self {
575            position,
576            expr_,
577            value_is_used: true,
578            id,
579        }
580    }
581
582    /// Helper for creating Invalid expressions.
583    pub fn invalid(position: Position, id: SyntaxId) -> Self {
584        Self {
585            position,
586            expr_: Expression_::Invalid,
587            value_is_used: true,
588            id,
589        }
590    }
591}
592
593#[derive(Debug, Clone, PartialEq, Eq)]
594pub struct Block {
595    pub open_brace: Position,
596    pub exprs: Vec<Rc<Expression>>,
597    pub close_brace: Position,
598}
599
600#[derive(Debug, Clone, PartialEq, Eq)]
601pub enum Visibility {
602    External(Position),
603    CurrentFile,
604}
605
606#[derive(Debug, Clone, PartialEq, Eq)]
607pub struct ToplevelExpression(pub Expression);
608
609#[derive(Debug, Clone, PartialEq, Eq)]
610pub struct FunInfo {
611    pub pos: Position,
612    pub doc_comment: Option<String>,
613    /// The name of the function. This is `None` for closures.
614    pub name_sym: Option<Symbol>,
615    /// If this is a toplevel function, the ID of the definition.
616    pub item_id: Option<ToplevelItemId>,
617    pub type_params: Vec<TypeSymbol>,
618    pub params: ParenthesizedParameters,
619    pub return_hint: Option<TypeHint>,
620    pub body: Block,
621}
622
623#[derive(Debug, Clone, PartialEq, Eq)]
624pub struct ParenthesizedParameters {
625    pub open_paren: Position,
626    pub params: Vec<SymbolWithHint>,
627    pub close_paren: Position,
628}
629
630#[derive(Debug, Clone, PartialEq, Eq)]
631pub struct TestInfo {
632    pub pos: Position,
633    pub doc_comment: Option<String>,
634    pub name_sym: Symbol,
635    pub body: Block,
636}
637
638#[derive(Debug, Clone, PartialEq, Eq)]
639pub struct VariantInfo {
640    pub name_sym: Symbol,
641    /// If this variant is of the form `Foo(T)`, the type hint inside
642    /// the parentheses.
643    pub payload_hint: Option<TypeHint>,
644}
645
646#[derive(Debug, Clone, PartialEq, Eq)]
647pub struct FieldInfo {
648    pub sym: Symbol,
649    pub hint: TypeHint,
650    pub doc_comment: Option<String>,
651}
652
653#[derive(Debug, Clone, PartialEq, Eq)]
654pub struct EnumInfo {
655    pub pos: Position,
656    pub visibility: Visibility,
657    pub doc_comment: Option<String>,
658    pub name_sym: TypeSymbol,
659    pub type_params: Vec<TypeSymbol>,
660    pub variants: Vec<VariantInfo>,
661}
662
663#[derive(Debug, Clone, PartialEq, Eq)]
664pub struct StructInfo {
665    pub pos: Position,
666    pub visibility: Visibility,
667    pub doc_comment: Option<String>,
668    pub name_sym: TypeSymbol,
669    pub type_params: Vec<TypeSymbol>,
670    /// The fields of this struct.
671    ///
672    /// We deliberately want an ordered data type here, so we can
673    /// display field information in the same order as the user
674    /// defined the fields.
675    pub fields: Vec<FieldInfo>,
676}
677
678#[derive(Debug, Clone, PartialEq, Eq)]
679pub struct ImportInfo {
680    pub pos: Position,
681    /// The actual path being imporrted. For example, if the user
682    /// wrote `import "./foo.gdn"`, then foo.gdn is the path here.
683    pub path: PathBuf,
684    /// The position of the string literal within
685    /// `import "./foo.gdn"`.
686    pub path_pos: Position,
687    pub id: SyntaxId,
688}
689
690#[derive(Debug, Clone, Copy, PartialEq, Eq)]
691pub enum BuiltinMethodKind {
692    ListAppend,
693    ListContains,
694    ListGet,
695    ListLen,
696    PathExists,
697    PathRead,
698    StringIndexOf,
699    StringLen,
700    StringLines,
701    StringSubstring,
702}
703
704#[derive(Debug, Clone, PartialEq, Eq)]
705pub enum MethodKind {
706    BuiltinMethod(BuiltinMethodKind, Option<FunInfo>),
707    UserDefinedMethod(FunInfo),
708}
709
710#[derive(Debug, Clone, PartialEq, Eq)]
711pub struct MethodInfo {
712    pub pos: Position,
713    /// The type that has this method.
714    pub receiver_hint: TypeHint,
715    /// The name of the receiver in the method definition. This is
716    /// typically `self`.
717    ///
718    /// TODO: this only exists for user-defined methods, so it's
719    /// clunky to have it for all methods.
720    pub receiver_sym: Symbol,
721    /// The name of the method itself, e.g. `len` in
722    /// `some_string.len()`.
723    pub name_sym: Symbol,
724    /// User-defined or built-in.
725    pub kind: MethodKind,
726}
727
728impl MethodInfo {
729    pub fn fun_info(&self) -> Option<&FunInfo> {
730        match &self.kind {
731            MethodKind::BuiltinMethod(_, fun_info) => fun_info.as_ref(),
732            MethodKind::UserDefinedMethod(fun_info) => Some(fun_info),
733        }
734    }
735
736    pub fn full_name(&self) -> String {
737        format!("{}::{}", self.receiver_hint.sym, self.name_sym.name)
738    }
739}
740
741#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
742pub struct ToplevelItemId(pub usize);
743
744#[derive(Debug, Clone, PartialEq, Eq)]
745pub enum ToplevelItem {
746    /// ```garden
747    /// fun foo() {}
748    /// external fun bar() {}
749    /// ```
750    Fun(Symbol, FunInfo, Visibility),
751    /// ```garden
752    /// fun (this: MyType) foo() {}
753    /// external fun (this: MyType) baz() {}
754    /// ```
755    Method(MethodInfo, Visibility),
756    /// ```garden
757    /// test foo { assert(1 == 2) }
758    /// ```
759    Test(TestInfo),
760    /// ```garden
761    /// enum Foo { Red, Green, Custom(String) }
762    /// ```
763    Enum(EnumInfo),
764    /// ```garden
765    /// struct Foo { x: Int, y: Int }
766    /// ```
767    Struct(StructInfo),
768    /// ```garden
769    /// import "./foo.gdn"
770    /// ```
771    Import(ImportInfo),
772    /// ```garden
773    /// println("hello world")
774    /// ```
775    ///
776    /// Should only be used in the REPL.
777    Expr(ToplevelExpression),
778    /// ```garden
779    /// { println("hello world") }
780    /// ```
781    Block(Block),
782}
783
784impl ToplevelItem {
785    pub fn position(&self) -> Position {
786        match self {
787            ToplevelItem::Fun(_, fun_info, _) => fun_info.pos.clone(),
788            ToplevelItem::Method(method_info, _) => method_info.pos.clone(),
789            ToplevelItem::Test(test_info) => test_info.pos.clone(),
790            ToplevelItem::Enum(enum_info) => enum_info.pos.clone(),
791            ToplevelItem::Struct(struct_info) => struct_info.pos.clone(),
792            ToplevelItem::Import(import_info) => import_info.pos.clone(),
793            ToplevelItem::Expr(toplevel_expression) => toplevel_expression.0.position.clone(),
794            ToplevelItem::Block(block) => Position::merge(&block.open_brace, &block.close_brace),
795        }
796    }
797
798    pub(crate) fn is_invalid_or_placeholder(&self) -> bool {
799        match self {
800            ToplevelItem::Fun(symbol, _, _) => symbol.is_placeholder(),
801            ToplevelItem::Method(method_info, _) => method_info.name_sym.is_placeholder(),
802            ToplevelItem::Test(test_info) => test_info.name_sym.is_placeholder(),
803            ToplevelItem::Enum(enum_info) => enum_info.name_sym.is_placeholder(),
804            ToplevelItem::Struct(struct_info) => struct_info.name_sym.is_placeholder(),
805            ToplevelItem::Expr(e) => e.0.expr_.is_invalid_or_placeholder(),
806            ToplevelItem::Block(_) => false,
807            ToplevelItem::Import(_) => false,
808        }
809    }
810}