tinymist_analysis/syntax/
def.rs

1//! Definitions of syntax structures.
2
3use core::fmt;
4use std::{
5    collections::BTreeMap,
6    ops::{Deref, Range},
7    sync::Arc,
8};
9
10use rustc_hash::FxHashMap;
11use serde::{Deserialize, Serialize};
12use tinymist_derive::DeclEnum;
13use tinymist_std::DefId;
14use tinymist_world::package::PackageSpec;
15use typst::{
16    foundations::{Element, Func, Module, Type, Value},
17    syntax::{Span, SyntaxNode, VirtualRoot},
18    utils::LazyHash,
19};
20
21use crate::{
22    adt::interner::impl_internable,
23    docs::DocString,
24    prelude::*,
25    ty::{InsTy, Interned, SelectTy, Ty, TypeVar},
26};
27
28use super::{ExprDescriber, ExprPrinter};
29
30/// Information about expressions in a source file.
31///
32/// This structure wraps expression analysis data and provides access to
33/// expression resolution, documentation, and scoping information.
34#[derive(Debug, Clone, Hash)]
35pub struct ExprInfo(Arc<LazyHash<ExprInfoRepr>>);
36
37impl ExprInfo {
38    /// Creates a new [`ExprInfo`] instance from expression information
39    /// representation.
40    ///
41    /// Wraps the provided representation in an Arc and LazyHash for efficient
42    /// sharing and hashing.
43    pub fn new(repr: ExprInfoRepr) -> Self {
44        Self(Arc::new(LazyHash::new(repr)))
45    }
46}
47
48impl Deref for ExprInfo {
49    type Target = Arc<LazyHash<ExprInfoRepr>>;
50
51    fn deref(&self) -> &Self::Target {
52        &self.0
53    }
54}
55
56/// Representation of [`ExprInfo`] for a specific file.
57///
58/// Contains all the analyzed information including resolution maps,
59/// documentation strings, imports, and exports.
60#[derive(Debug)]
61pub struct ExprInfoRepr {
62    /// The file ID this expression information belongs to.
63    pub fid: TypstFileId,
64    /// Revision number for tracking changes to the file.
65    pub revision: usize,
66    /// The source code content.
67    pub source: Source,
68    /// The root expression of the file.
69    pub root: Expr,
70    /// Documentation string for the module.
71    pub module_docstring: Arc<DocString>,
72    /// The lexical scope of exported symbols from this file.
73    pub exports: Arc<LazyHash<LexicalScope>>,
74    /// Map from file IDs to imported lexical scopes.
75    pub imports: FxHashMap<TypstFileId, Arc<LazyHash<LexicalScope>>>,
76    /// Map from spans to expressions for scope analysis.
77    pub exprs: FxHashMap<Span, Expr>,
78    /// Map from spans to resolved reference expressions.
79    pub resolves: FxHashMap<Span, Interned<RefExpr>>,
80    /// Map from declarations to their documentation strings.
81    pub docstrings: FxHashMap<DeclExpr, Arc<DocString>>,
82    /// Layout information for module import items in this file.
83    pub module_items: FxHashMap<Interned<Decl>, ModuleItemLayout>,
84}
85
86impl std::hash::Hash for ExprInfoRepr {
87    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
88        // already contained in the source.
89        // self.fid.hash(state);
90        self.revision.hash(state);
91        self.source.hash(state);
92        self.root.hash(state);
93        self.exports.hash(state);
94        let mut resolves = self.resolves.iter().collect::<Vec<_>>();
95        resolves.sort_by_key(|(fid, _)| fid.into_raw());
96        resolves.hash(state);
97        let mut imports = self.imports.iter().collect::<Vec<_>>();
98        imports.sort_by_key(|(fid, _)| fid.into_raw());
99        imports.hash(state);
100        let mut module_items = self.module_items.iter().collect::<Vec<_>>();
101        module_items.sort_by_key(|(decl, _)| decl.span().into_raw());
102        module_items.hash(state);
103    }
104}
105
106impl ExprInfoRepr {
107    /// Gets the definition expression for a given declaration.
108    pub fn get_def(&self, decl: &Interned<Decl>) -> Option<Expr> {
109        if decl.is_def() {
110            return Some(Expr::Decl(decl.clone()));
111        }
112        let resolved = self.resolves.get(&decl.span())?;
113        Some(Expr::Ref(resolved.clone()))
114    }
115
116    /// Gets all references to a given declaration.
117    pub fn get_refs(
118        &self,
119        decl: Interned<Decl>,
120    ) -> impl Iterator<Item = (&Span, &Interned<RefExpr>)> {
121        let of = Some(Expr::Decl(decl.clone()));
122        self.resolves
123            .iter()
124            .filter(move |(_, r)| match (decl.as_ref(), r.decl.as_ref()) {
125                (Decl::Label(..), Decl::Label(..))
126                | (Decl::Label(..), Decl::ContentRef(..))
127                | (Decl::ContentRef(..), Decl::Label(..))
128                | (Decl::ContentRef(..), Decl::ContentRef(..)) => r.decl.name() == decl.name(),
129                (Decl::Label(..), _) => false,
130                _ => r.decl == decl || r.root == of,
131            })
132    }
133
134    /// Checks if a declaration is exported from this module.
135    pub fn is_exported(&self, decl: &Interned<Decl>) -> bool {
136        let of = Expr::Decl(decl.clone());
137        self.exports
138            .get(decl.name())
139            .is_some_and(|export| match export {
140                Expr::Ref(ref_expr) => ref_expr.root == Some(of),
141                exprt => *exprt == of,
142            })
143    }
144
145    /// Shows the expression information.
146    #[allow(dead_code)]
147    fn show(&self) {
148        use std::io::Write;
149        let vpath = self
150            .fid
151            .vpath()
152            .realize(Path::new("target/exprs/"))
153            .expect("expression dump path must be realizable");
154        let root = vpath.with_extension("root.expr");
155        std::fs::create_dir_all(root.parent().unwrap()).unwrap();
156        std::fs::write(root, format!("{}", self.root)).unwrap();
157        let scopes = vpath.with_extension("scopes.expr");
158        std::fs::create_dir_all(scopes.parent().unwrap()).unwrap();
159        {
160            let mut scopes = std::fs::File::create(scopes).unwrap();
161            for (span, expr) in self.exprs.iter() {
162                writeln!(scopes, "{span:?} -> {expr}").unwrap();
163            }
164        }
165        let imports = vpath.with_extension("imports.expr");
166        std::fs::create_dir_all(imports.parent().unwrap()).unwrap();
167        std::fs::write(imports, format!("{:#?}", self.imports)).unwrap();
168        let exports = vpath.with_extension("exports.expr");
169        std::fs::create_dir_all(exports.parent().unwrap()).unwrap();
170        std::fs::write(exports, format!("{:#?}", self.exports)).unwrap();
171    }
172}
173
174/// Describes how an import item is laid out in the source text.
175#[derive(Debug, Clone, Hash)]
176pub struct ModuleItemLayout {
177    /// The module declaration that owns this item.
178    pub parent: Interned<Decl>,
179    /// The byte range covering the whole `foo as bar` clause.
180    pub item_range: Range<usize>,
181    /// The byte range covering the bound identifier (`bar` in `foo as bar`).
182    pub binding_range: Range<usize>,
183}
184
185/// Represents different kinds of expressions in the language.
186///
187/// This enum covers all possible expression types that can appear in Typst
188/// source code, from basic literals to complex control flow constructs.
189#[derive(Debug, Clone, PartialEq, Eq, Hash)]
190pub enum Expr {
191    /// A sequence of expressions: `{ x; y; z }`
192    Block(Interned<Vec<Expr>>),
193    /// An array literal: `(1, 2, 3)`
194    Array(Interned<ArgsExpr>),
195    /// A dict literal: `(a: 1, b: 2)`
196    Dict(Interned<ArgsExpr>),
197    /// An args literal: `(1, 2, 3)`
198    Args(Interned<ArgsExpr>),
199    /// A pattern: `(x, y, ..z)`
200    Pattern(Interned<Pattern>),
201    /// An element literal: `[*Hi* there!]`
202    Element(Interned<ElementExpr>),
203    /// An unary operation: `-x`
204    Unary(Interned<UnExpr>),
205    /// A binary operation: `x + y`
206    Binary(Interned<BinExpr>),
207    /// A function call: `f(x, y)`
208    Apply(Interned<ApplyExpr>),
209    /// A function: `(x, y) => x + y`
210    Func(Interned<FuncExpr>),
211    /// A let: `let x = 1`
212    Let(Interned<LetExpr>),
213    /// A show: `show heading: it => emph(it.body)`
214    Show(Interned<ShowExpr>),
215    /// A set: `set text(...)`
216    Set(Interned<SetExpr>),
217    /// A reference: `#x`
218    Ref(Interned<RefExpr>),
219    /// A content reference: `@x`
220    ContentRef(Interned<ContentRefExpr>),
221    /// A select: `x.y`
222    Select(Interned<SelectExpr>),
223    /// An import expression: `import "path.typ": x`
224    Import(Interned<ImportExpr>),
225    /// An include expression: `include "path.typ"`
226    Include(Interned<IncludeExpr>),
227    /// A contextual expression: `context text.lang`
228    Contextual(Interned<Expr>),
229    /// A conditional expression: `if x { y } else { z }`
230    Conditional(Interned<IfExpr>),
231    /// A while loop: `while x { y }`
232    WhileLoop(Interned<WhileExpr>),
233    /// A for loop: `for x in y { z }`
234    ForLoop(Interned<ForExpr>),
235    /// A type: `str`
236    Type(Ty),
237    /// A declaration: `x`
238    Decl(DeclExpr),
239    /// A star import: `*`
240    Star,
241}
242
243impl Expr {
244    /// Returns a string representation of the expression.
245    pub fn repr(&self) -> EcoString {
246        let mut s = EcoString::new();
247        let _ = ExprDescriber::new(&mut s).write_expr(self);
248        s
249    }
250
251    /// Returns the span location of the expression.
252    pub fn span(&self) -> Span {
253        match self {
254            Expr::Decl(decl) => decl.span(),
255            Expr::Select(select) => select.span,
256            Expr::Apply(apply) => apply.span,
257            _ => Span::detached(),
258        }
259    }
260
261    /// Returns the file ID associated with this expression, if any.
262    pub fn file_id(&self) -> Option<TypstFileId> {
263        match self {
264            Expr::Decl(decl) => decl.file_id(),
265            _ => self.span().id(),
266        }
267    }
268
269    /// Returns whether the expression is definitely defined.
270    pub fn is_defined(&self) -> bool {
271        match self {
272            Expr::Ref(refs) => refs.root.is_some() || refs.term.is_some(),
273            Expr::Decl(decl) => decl.is_def(),
274            // There are unsure cases, like `x.y`, which may be defined or not.
275            _ => false,
276        }
277    }
278}
279
280impl fmt::Display for Expr {
281    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
282        ExprPrinter::new(f).write_expr(self)
283    }
284}
285
286/// Type alias for lexical scopes.
287///
288/// Represents a lexical scope as a persistent map from names to expressions.
289pub type LexicalScope = rpds::RedBlackTreeMapSync<Interned<str>, Expr>;
290
291/// Different types of scopes for expression evaluation.
292///
293/// Represents the various kinds of scopes that can contain variable bindings,
294/// including lexical scopes, modules, functions, and types.
295#[derive(Debug, Clone)]
296pub enum ExprScope {
297    /// A lexical scope extracted from a source file.
298    Lexical(LexicalScope),
299    /// A module instance which is either built-in or evaluated during analysis.
300    Module(Module),
301    /// A scope bound to a function.
302    Func(Func),
303    /// A scope bound to a type.
304    Type(Type),
305}
306
307impl ExprScope {
308    /// Creates an empty lexical scope.
309    pub fn empty() -> Self {
310        ExprScope::Lexical(LexicalScope::default())
311    }
312
313    /// Checks if the scope contains no bindings.
314    pub fn is_empty(&self) -> bool {
315        match self {
316            ExprScope::Lexical(scope) => scope.is_empty(),
317            ExprScope::Module(module) => is_empty_scope(module.scope()),
318            ExprScope::Func(func) => func.scope().is_none_or(is_empty_scope),
319            ExprScope::Type(ty) => is_empty_scope(ty.scope()),
320        }
321    }
322
323    /// Looks up a name in the scope and returns both expression and type
324    /// information.
325    pub fn get(&self, name: &Interned<str>) -> (Option<Expr>, Option<Ty>) {
326        let (of, val) = match self {
327            ExprScope::Lexical(scope) => {
328                crate::log_debug_ct!("evaluating: {name:?} in {scope:?}");
329                (scope.get(name).cloned(), None)
330            }
331            ExprScope::Module(module) => {
332                let v = module.scope().get(name);
333                // let decl =
334                //     v.and_then(|_| Some(Decl::external(module.file_id()?,
335                // name.clone()).into()));
336                (None, v)
337            }
338            ExprScope::Func(func) => (None, func.scope().unwrap().get(name)),
339            ExprScope::Type(ty) => (None, ty.scope().get(name)),
340        };
341
342        // ref_expr.of = of.clone();
343        // ref_expr.val = val.map(|v| Ty::Value(InsTy::new(v.clone())));
344        // return ref_expr;
345        (
346            of,
347            val.cloned()
348                .map(|val| Ty::Value(InsTy::new(val.read().to_owned()))),
349        )
350    }
351
352    /// Merges all bindings from this scope into the provided export map.
353    pub fn merge_into(&self, exports: &mut LexicalScope) {
354        match self {
355            ExprScope::Lexical(scope) => {
356                for (name, expr) in scope.iter() {
357                    exports.insert_mut(name.clone(), expr.clone());
358                }
359            }
360            ExprScope::Module(module) => {
361                crate::log_debug_ct!("imported: {module:?}");
362                let v = Interned::new(Ty::Value(InsTy::new(Value::Module(module.clone()))));
363                for (name, _) in module.scope().iter() {
364                    let name: Interned<str> = name.into();
365                    exports.insert_mut(name.clone(), select_of(v.clone(), name));
366                }
367            }
368            ExprScope::Func(func) => {
369                if let Some(scope) = func.scope() {
370                    let v = Interned::new(Ty::Value(InsTy::new(Value::Func(func.clone()))));
371                    for (name, _) in scope.iter() {
372                        let name: Interned<str> = name.into();
373                        exports.insert_mut(name.clone(), select_of(v.clone(), name));
374                    }
375                }
376            }
377            ExprScope::Type(ty) => {
378                let v = Interned::new(Ty::Value(InsTy::new(Value::Type(*ty))));
379                for (name, _) in ty.scope().iter() {
380                    let name: Interned<str> = name.into();
381                    exports.insert_mut(name.clone(), select_of(v.clone(), name));
382                }
383            }
384        }
385    }
386}
387
388fn select_of(source: Interned<Ty>, name: Interned<str>) -> Expr {
389    Expr::Type(Ty::Select(SelectTy::new(source, name)))
390}
391
392/// Kind of a definition.
393#[derive(Debug, Default, Clone, Copy, Hash, Serialize, Deserialize)]
394#[serde(rename_all = "camelCase")]
395pub enum DefKind {
396    /// A definition for some constant: `let x = 1`
397    #[default]
398    Constant,
399    /// A definition for some function: `(x, y) => x + y`
400    Function,
401    /// A definition for some variable: `let x = (x, y) => x + y`
402    Variable,
403    /// A definition for some module.
404    Module,
405    /// A definition for some struct (type).
406    Struct,
407    /// A definition for some reference: `<label>`
408    Reference,
409}
410
411impl fmt::Display for DefKind {
412    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
413        match self {
414            Self::Constant => write!(f, "constant"),
415            Self::Function => write!(f, "function"),
416            Self::Variable => write!(f, "variable"),
417            Self::Module => write!(f, "module"),
418            Self::Struct => write!(f, "struct"),
419            Self::Reference => write!(f, "reference"),
420        }
421    }
422}
423
424/// Type alias for declaration expressions.
425pub type DeclExpr = Interned<Decl>;
426
427/// Represents different kinds of declarations in the language.
428#[derive(Clone, PartialEq, Eq, Hash, DeclEnum)]
429pub enum Decl {
430    /// A function declaration: `(x, y) => x + y`
431    Func(SpannedDecl),
432    /// An import alias declaration: `import "path.typ": x`
433    ImportAlias(SpannedDecl),
434    /// A variable declaration: `let x = 1`
435    Var(SpannedDecl),
436    /// An identifier reference declaration: `x`
437    IdentRef(SpannedDecl),
438    /// A module declaration: `import calc`
439    Module(ModuleDecl),
440    /// A module alias declaration: `import "path.typ" as x`
441    ModuleAlias(SpannedDecl),
442    /// A path stem declaration: `path.typ`
443    PathStem(SpannedDecl),
444    /// An import path declaration: `import "path.typ"`
445    ImportPath(SpannedDecl),
446    /// An include path declaration: `include "path.typ"`
447    IncludePath(SpannedDecl),
448    /// An import declaration: `import "path.typ"`
449    Import(SpannedDecl),
450    /// A content reference declaration: `@x`
451    ContentRef(SpannedDecl),
452    /// A label declaration: `label`
453    Label(SpannedDecl),
454    /// A string name declaration: `"x"`
455    StrName(SpannedDecl),
456    /// A module import declaration: `import "path.typ": *`
457    ModuleImport(SpanDecl),
458    /// A closure declaration: `(x, y) => x + y`
459    Closure(SpanDecl),
460    /// A pattern declaration: `let (x, y, ..z) = 1`
461    Pattern(SpanDecl),
462    /// A spread declaration: `..z`
463    Spread(SpanDecl),
464    /// A content declaration: `#[text]`
465    Content(SpanDecl),
466    /// A constant declaration: `let x = 1`
467    Constant(SpanDecl),
468    /// A bib entry declaration: `@entry`
469    BibEntry(NameRangeDecl),
470    /// A docs declaration created by the compiler.
471    Docs(DocsDecl),
472    /// A generated declaration created by the compiler.
473    Generated(GeneratedDecl),
474}
475
476impl Decl {
477    /// Creates a function declaration from an identifier.
478    pub fn func(ident: ast::Ident) -> Self {
479        Self::Func(SpannedDecl {
480            name: ident.get().into(),
481            at: ident.span(),
482        })
483    }
484
485    /// Creates a variable declaration from a string literal.
486    pub fn lit(name: &str) -> Self {
487        Self::Var(SpannedDecl {
488            name: name.into(),
489            at: Span::detached(),
490        })
491    }
492
493    /// Creates a variable declaration from an interned string.
494    pub fn lit_(name: Interned<str>) -> Self {
495        Self::Var(SpannedDecl {
496            name,
497            at: Span::detached(),
498        })
499    }
500
501    /// Creates a variable declaration from an identifier.
502    pub fn var(ident: ast::Ident) -> Self {
503        Self::Var(SpannedDecl {
504            name: ident.get().into(),
505            at: ident.span(),
506        })
507    }
508
509    /// Creates an import alias declaration from an identifier.
510    pub fn import_alias(ident: ast::Ident) -> Self {
511        Self::ImportAlias(SpannedDecl {
512            name: ident.get().into(),
513            at: ident.span(),
514        })
515    }
516
517    /// Creates an identifier reference declaration from an identifier.
518    pub fn ident_ref(ident: ast::Ident) -> Self {
519        Self::IdentRef(SpannedDecl {
520            name: ident.get().into(),
521            at: ident.span(),
522        })
523    }
524
525    /// Creates an identifier reference declaration from a math identifier.
526    pub fn math_ident_ref(ident: ast::MathIdent) -> Self {
527        Self::IdentRef(SpannedDecl {
528            name: ident.get().into(),
529            at: ident.span(),
530        })
531    }
532
533    /// Creates a module declaration with a file ID.
534    pub fn module(fid: TypstFileId) -> Self {
535        let name = {
536            let stem = fid.vpath().as_rooted_path_compat().file_stem();
537            stem.and_then(|s| Some(Interned::new_str(s.to_str()?)))
538                .unwrap_or_default()
539        };
540        Self::Module(ModuleDecl { name, fid })
541    }
542
543    /// Creates a module declaration with a name and a file ID.
544    pub fn module_with_name(name: Interned<str>, fid: TypstFileId) -> Self {
545        Self::Module(ModuleDecl { name, fid })
546    }
547
548    /// Creates a module alias declaration from an identifier.
549    pub fn module_alias(ident: ast::Ident) -> Self {
550        Self::ModuleAlias(SpannedDecl {
551            name: ident.get().into(),
552            at: ident.span(),
553        })
554    }
555
556    /// Creates an import declaration from an identifier.
557    pub fn import(ident: ast::Ident) -> Self {
558        Self::Import(SpannedDecl {
559            name: ident.get().into(),
560            at: ident.span(),
561        })
562    }
563
564    /// Creates a label declaration with a name and span.
565    pub fn label(name: &str, at: Span) -> Self {
566        Self::Label(SpannedDecl {
567            name: name.into(),
568            at,
569        })
570    }
571
572    /// Creates a content reference declaration from a reference AST node.
573    pub fn ref_(ident: ast::Ref) -> Self {
574        Self::ContentRef(SpannedDecl {
575            name: ident.target().into(),
576            at: {
577                let marker_span = ident
578                    .to_untyped()
579                    .children()
580                    .find(|child| child.kind() == SyntaxKind::RefMarker)
581                    .map(|child| child.span());
582
583                marker_span.unwrap_or(ident.span())
584            },
585        })
586    }
587
588    /// Creates a string name declaration from a syntax node and name.
589    pub fn str_name(s: SyntaxNode, name: &str) -> Decl {
590        Self::StrName(SpannedDecl {
591            name: name.into(),
592            at: s.span(),
593        })
594    }
595
596    /// Calculates the path stem from a string path or package specification.
597    ///
598    /// For package specs (starting with '@'), extracts the package name.
599    /// For file paths, extracts the file stem. Returns empty string if
600    /// extraction fails.
601    pub fn calc_path_stem(s: &str) -> Interned<str> {
602        use std::str::FromStr;
603        let name = if s.starts_with('@') {
604            let spec = PackageSpec::from_str(s).ok();
605            spec.map(|spec| Interned::new_str(spec.name.as_str()))
606        } else {
607            let stem = Path::new(s).file_stem();
608            stem.and_then(|stem| Some(Interned::new_str(stem.to_str()?)))
609        };
610        name.unwrap_or_default()
611    }
612
613    /// Creates a path stem declaration from a syntax node and name.
614    pub fn path_stem(s: SyntaxNode, name: Interned<str>) -> Self {
615        Self::PathStem(SpannedDecl { name, at: s.span() })
616    }
617
618    /// Creates an import path declaration with a span and name.
619    pub fn import_path(s: Span, name: Interned<str>) -> Self {
620        Self::ImportPath(SpannedDecl { name, at: s })
621    }
622
623    /// Creates an include path declaration with a span and name.
624    pub fn include_path(s: Span, name: Interned<str>) -> Self {
625        Self::IncludePath(SpannedDecl { name, at: s })
626    }
627
628    /// Creates a module import declaration with just a span.
629    pub fn module_import(s: Span) -> Self {
630        Self::ModuleImport(SpanDecl(s))
631    }
632
633    /// Creates a closure declaration with just a span.
634    pub fn closure(s: Span) -> Self {
635        Self::Closure(SpanDecl(s))
636    }
637
638    /// Creates a pattern declaration with just a span.
639    pub fn pattern(s: Span) -> Self {
640        Self::Pattern(SpanDecl(s))
641    }
642
643    /// Creates a spread declaration with just a span.
644    pub fn spread(s: Span) -> Self {
645        Self::Spread(SpanDecl(s))
646    }
647
648    /// Creates a content declaration with just a span.
649    pub fn content(s: Span) -> Self {
650        Self::Content(SpanDecl(s))
651    }
652
653    /// Creates a constant declaration with just a span.
654    pub fn constant(s: Span) -> Self {
655        Self::Constant(SpanDecl(s))
656    }
657
658    /// Creates a documentation declaration linking a base declaration with a
659    /// type variable.
660    pub fn docs(base: Interned<Decl>, var: Interned<TypeVar>) -> Self {
661        Self::Docs(DocsDecl { base, var })
662    }
663
664    /// Creates a generated declaration with a definition ID.
665    pub fn generated(def_id: DefId) -> Self {
666        Self::Generated(GeneratedDecl(def_id))
667    }
668
669    /// Creates a bibliography entry declaration.
670    pub fn bib_entry(
671        name: Interned<str>,
672        fid: TypstFileId,
673        name_range: Range<usize>,
674        range: Option<Range<usize>>,
675    ) -> Self {
676        Self::BibEntry(NameRangeDecl {
677            name,
678            at: Box::new((fid, name_range, range)),
679        })
680    }
681
682    /// Checks if this declaration represents a definition rather than a
683    /// reference (usage of a definition).
684    pub fn is_def(&self) -> bool {
685        matches!(
686            self,
687            Self::Func(..)
688                | Self::BibEntry(..)
689                | Self::Closure(..)
690                | Self::Var(..)
691                | Self::Label(..)
692                | Self::StrName(..)
693                | Self::Module(..)
694                | Self::ModuleImport(..)
695                | Self::PathStem(..)
696                | Self::ImportPath(..)
697                | Self::IncludePath(..)
698                | Self::Spread(..)
699                | Self::Generated(..)
700        )
701    }
702
703    /// Returns the kind of definition this declaration represents.
704    pub fn kind(&self) -> DefKind {
705        use Decl::*;
706        match self {
707            ModuleAlias(..) | Module(..) | PathStem(..) | ImportPath(..) | IncludePath(..) => {
708                DefKind::Module
709            }
710            // Type(_) => DocStringKind::Struct,
711            Func(..) | Closure(..) => DefKind::Function,
712            Label(..) | BibEntry(..) | ContentRef(..) => DefKind::Reference,
713            IdentRef(..) | ImportAlias(..) | Import(..) | Var(..) => DefKind::Variable,
714            Pattern(..) | Docs(..) | Generated(..) | Constant(..) | StrName(..)
715            | ModuleImport(..) | Content(..) | Spread(..) => DefKind::Constant,
716        }
717    }
718
719    /// Gets file location of the declaration.
720    pub fn file_id(&self) -> Option<TypstFileId> {
721        match self {
722            Self::Module(ModuleDecl { fid, .. }) => Some(*fid),
723            Self::BibEntry(NameRangeDecl { at, .. }) => Some(at.0),
724            Self::Docs(DocsDecl { base, .. }) => base.file_id(),
725            that => that.span().id(),
726        }
727    }
728
729    /// Gets full range of the declaration.
730    pub fn full_range(&self) -> Option<Range<usize>> {
731        if let Decl::BibEntry(decl) = self {
732            return decl.at.2.clone();
733        }
734
735        None
736    }
737
738    /// Creates a reference expression that points to this declaration.
739    pub fn as_def(this: &Interned<Self>, val: Option<Ty>) -> Interned<RefExpr> {
740        let def: Expr = this.clone().into();
741        Interned::new(RefExpr {
742            decl: this.clone(),
743            step: Some(def.clone()),
744            root: Some(def),
745            term: val,
746        })
747    }
748}
749
750impl Ord for Decl {
751    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
752        let base = match (self, other) {
753            (Self::Generated(l), Self::Generated(r)) => l.0.0.cmp(&r.0.0),
754            (Self::Module(l), Self::Module(r)) => l.fid.into_raw().cmp(&r.fid.into_raw()),
755            (Self::Docs(l), Self::Docs(r)) => l.var.cmp(&r.var).then_with(|| l.base.cmp(&r.base)),
756            _ => self.span().into_raw().cmp(&other.span().into_raw()),
757        };
758
759        base.then_with(|| self.name().cmp(other.name()))
760    }
761}
762
763trait StrictCmp {
764    /// Low-performance comparison but it is free from the concurrency issue.
765    /// This is only used for making stable test snapshots.
766    fn strict_cmp(&self, other: &Self) -> std::cmp::Ordering;
767}
768
769impl Decl {
770    /// Low-performance comparison that is free from concurrency issues.
771    pub fn strict_cmp(&self, other: &Self) -> std::cmp::Ordering {
772        let base = match (self, other) {
773            (Self::Generated(l), Self::Generated(r)) => l.0.0.cmp(&r.0.0),
774            (Self::Module(l), Self::Module(r)) => l.fid.strict_cmp(&r.fid),
775            (Self::Docs(l), Self::Docs(r)) => l
776                .var
777                .strict_cmp(&r.var)
778                .then_with(|| l.base.strict_cmp(&r.base)),
779            _ => self.span().strict_cmp(&other.span()),
780        };
781
782        base.then_with(|| self.name().cmp(other.name()))
783    }
784}
785
786impl StrictCmp for TypstFileId {
787    fn strict_cmp(&self, other: &Self) -> std::cmp::Ordering {
788        fn root_cmp(left: &VirtualRoot, right: &VirtualRoot) -> std::cmp::Ordering {
789            match (left, right) {
790                (VirtualRoot::Project, VirtualRoot::Project) => std::cmp::Ordering::Equal,
791                (VirtualRoot::Project, VirtualRoot::Package(_)) => std::cmp::Ordering::Less,
792                (VirtualRoot::Package(_), VirtualRoot::Project) => std::cmp::Ordering::Greater,
793                (VirtualRoot::Package(left), VirtualRoot::Package(right)) => left
794                    .namespace
795                    .cmp(&right.namespace)
796                    .then_with(|| left.name.cmp(&right.name))
797                    .then_with(|| left.version.cmp(&right.version)),
798            }
799        }
800
801        root_cmp(self.root(), other.root()).then_with(|| {
802            self.vpath()
803                .get_with_slash()
804                .cmp(other.vpath().get_with_slash())
805        })
806    }
807}
808impl<T: StrictCmp> StrictCmp for Option<T> {
809    fn strict_cmp(&self, other: &Self) -> std::cmp::Ordering {
810        match (self, other) {
811            (Some(l), Some(r)) => l.strict_cmp(r),
812            (Some(_), None) => std::cmp::Ordering::Greater,
813            (None, Some(_)) => std::cmp::Ordering::Less,
814            (None, None) => std::cmp::Ordering::Equal,
815        }
816    }
817}
818
819impl StrictCmp for Span {
820    fn strict_cmp(&self, other: &Self) -> std::cmp::Ordering {
821        self.id()
822            .strict_cmp(&other.id())
823            .then_with(|| self.into_raw().cmp(&other.into_raw()))
824    }
825}
826
827impl PartialOrd for Decl {
828    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
829        Some(self.cmp(other))
830    }
831}
832
833impl From<Decl> for Expr {
834    fn from(decl: Decl) -> Self {
835        Expr::Decl(decl.into())
836    }
837}
838
839impl From<DeclExpr> for Expr {
840    fn from(decl: DeclExpr) -> Self {
841        Expr::Decl(decl)
842    }
843}
844
845/// A declaration with an associated name and span location.
846#[derive(Clone, PartialEq, Eq, Hash)]
847pub struct SpannedDecl {
848    /// The name of the declaration.
849    name: Interned<str>,
850    /// The span location of the declaration.
851    at: Span,
852}
853
854impl SpannedDecl {
855    /// Gets the name of the declaration.
856    fn name(&self) -> &Interned<str> {
857        &self.name
858    }
859
860    /// Gets the span location of the declaration.
861    fn span(&self) -> Span {
862        self.at
863    }
864}
865
866impl fmt::Debug for SpannedDecl {
867    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
868        f.write_str(self.name.as_ref())
869    }
870}
871
872/// A declaration with a name and range information.
873#[derive(Clone, PartialEq, Eq, Hash)]
874pub struct NameRangeDecl {
875    /// The name of the declaration.
876    pub name: Interned<str>,
877    /// Boxed tuple containing (file_id, name_range, full_range).
878    pub at: Box<(TypstFileId, Range<usize>, Option<Range<usize>>)>,
879}
880
881impl NameRangeDecl {
882    /// Gets the name of the declaration.
883    fn name(&self) -> &Interned<str> {
884        &self.name
885    }
886
887    /// Gets the span location of the declaration.
888    fn span(&self) -> Span {
889        Span::detached()
890    }
891}
892
893impl fmt::Debug for NameRangeDecl {
894    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
895        f.write_str(self.name.as_ref())
896    }
897}
898
899/// A module declaration with name and file ID.
900#[derive(Clone, PartialEq, Eq, Hash)]
901pub struct ModuleDecl {
902    /// The name of the module.
903    pub name: Interned<str>,
904    /// The file ID where the module is defined.
905    pub fid: TypstFileId,
906}
907
908impl ModuleDecl {
909    /// Gets the name of the declaration.
910    fn name(&self) -> &Interned<str> {
911        &self.name
912    }
913
914    /// Gets the span location of the declaration.
915    fn span(&self) -> Span {
916        Span::detached()
917    }
918}
919
920impl fmt::Debug for ModuleDecl {
921    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
922        f.write_str(self.name.as_ref())
923    }
924}
925
926/// A documentation declaration linking a base declaration with type variables.
927#[derive(Clone, PartialEq, Eq, Hash)]
928pub struct DocsDecl {
929    base: Interned<Decl>,
930    var: Interned<TypeVar>,
931}
932
933impl DocsDecl {
934    /// Gets the name of the declaration.
935    fn name(&self) -> &Interned<str> {
936        Interned::empty()
937    }
938
939    /// Gets the span location of the declaration.
940    fn span(&self) -> Span {
941        Span::detached()
942    }
943}
944
945impl fmt::Debug for DocsDecl {
946    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
947        write!(f, "{:?}, {:?}", self.base, self.var)
948    }
949}
950
951/// A span-only declaration for anonymous constructs.
952#[derive(Clone, PartialEq, Eq, Hash)]
953pub struct SpanDecl(Span);
954
955impl SpanDecl {
956    /// Gets the name of the declaration.
957    fn name(&self) -> &Interned<str> {
958        Interned::empty()
959    }
960
961    /// Gets the span location of the declaration.
962    fn span(&self) -> Span {
963        self.0
964    }
965}
966
967impl fmt::Debug for SpanDecl {
968    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
969        write!(f, "..")
970    }
971}
972
973/// A generated declaration with a unique definition ID.
974#[derive(Clone, PartialEq, Eq, Hash)]
975pub struct GeneratedDecl(DefId);
976
977impl GeneratedDecl {
978    /// Gets the name of the declaration.
979    fn name(&self) -> &Interned<str> {
980        Interned::empty()
981    }
982
983    /// Gets the span location of the declaration.
984    fn span(&self) -> Span {
985        Span::detached()
986    }
987}
988
989impl fmt::Debug for GeneratedDecl {
990    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
991        self.0.fmt(f)
992    }
993}
994
995/// Type alias for unary expressions.
996pub type UnExpr = UnInst<Expr>;
997/// Type alias for binary expressions.
998pub type BinExpr = BinInst<Expr>;
999
1000/// Type alias for export maps.
1001///
1002/// Maps exported names to their corresponding expressions.
1003pub type ExportMap = BTreeMap<Interned<str>, Expr>;
1004
1005/// Represents different kinds of function arguments.
1006///
1007/// Covers positional arguments, named arguments, and spread arguments.
1008#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1009pub enum ArgExpr {
1010    /// A positional argument: `x`
1011    Pos(Expr),
1012    /// A named argument: `a: x`
1013    Named(Box<(DeclExpr, Expr)>),
1014    /// A named argument with a default value: `((a): x)`
1015    NamedRt(Box<(Expr, Expr)>),
1016    /// A spread argument: `..x`
1017    Spread(Expr),
1018}
1019
1020/// Represents different kinds of patterns for destructuring.
1021#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1022pub enum Pattern {
1023    /// A general pattern expression can occur in right-hand side of a
1024    /// function signature.
1025    Expr(Expr),
1026    /// A simple pattern: `x`
1027    Simple(Interned<Decl>),
1028    /// A pattern signature: `(x, y: val, ..z)`
1029    Sig(Box<PatternSig>),
1030}
1031
1032impl fmt::Display for Pattern {
1033    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1034        ExprPrinter::new(f).write_pattern(self)
1035    }
1036}
1037
1038impl Pattern {
1039    /// Returns a string representation of the pattern.
1040    pub fn repr(&self) -> EcoString {
1041        let mut s = EcoString::new();
1042        let _ = ExprDescriber::new(&mut s).write_pattern(self);
1043        s
1044    }
1045}
1046
1047/// Signature pattern for function parameters.
1048///
1049/// Describes the structure of function parameters including positional,
1050/// named, and spread parameters.
1051#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1052pub struct PatternSig {
1053    /// Positional parameters in order.
1054    pub pos: EcoVec<Interned<Pattern>>,
1055    /// Named parameters with their default patterns.
1056    pub named: EcoVec<(DeclExpr, Interned<Pattern>)>,
1057    /// Left spread parameter (collects extra positional arguments).
1058    pub spread_left: Option<(DeclExpr, Interned<Pattern>)>,
1059    /// Right spread parameter (collects remaining arguments).
1060    pub spread_right: Option<(DeclExpr, Interned<Pattern>)>,
1061}
1062
1063impl Pattern {}
1064
1065impl_internable!(Decl,);
1066
1067/// Represents a content sequence expression.
1068///
1069/// Used for sequences of content elements with associated type information.
1070#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1071pub struct ContentSeqExpr {
1072    /// The type of the content sequence
1073    pub ty: Ty,
1074}
1075
1076/// Represents a reference expression.
1077///
1078/// A reference expression tracks how an identifier resolves through the lexical
1079/// scope, imports, and field accesses. It maintains a chain of resolution steps
1080/// to support features like go-to-definition, go-to-reference, and type
1081/// inference.
1082///
1083/// # Resolution Chain
1084///
1085/// The fields form a resolution chain: `root` -> `step` -> `decl`, where:
1086/// - `root` is the original source of the value
1087/// - `step` is any intermediate transformation
1088/// - `decl` is the final identifier being referenced
1089/// - `term` is the resolved type (if known)
1090///   - Hint: A value `1`'s typst type is `int`, but here we keep the type as
1091///     `1` to improve the type inference.
1092///
1093/// # Examples
1094///
1095/// ## Simple identifier reference
1096/// ```rust,ignore
1097/// // For: let x = value; let y = x;
1098/// RefExpr {
1099///     decl: y,           // The identifier 'y'
1100///     root: Some(x),     // Points back to 'x'
1101///     step: Some(x),     // Same as root for simple refs
1102///     term: None,        // Type may not be known yet
1103/// }
1104/// ```
1105///
1106/// ## Import with rename
1107/// ```rust,ignore
1108/// // For: import "mod.typ": old as new
1109/// // First creates ref for 'old':
1110/// RefExpr { decl: old, root: Some(mod.old), step: Some(field), term: Some(Func(() -> dict)) }
1111/// // Then creates ref for 'new':
1112/// RefExpr { decl: new, root: Some(mod.old), step: Some(old), term: Some(Func(() -> dict)) }
1113/// ```
1114///
1115/// ## Builtin definitions
1116/// ```rust,ignore
1117/// // For: std.length
1118/// RefExpr { decl: length, root: None, step: None, term: Some(Type(length)) }
1119/// ```
1120#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1121pub struct RefExpr {
1122    /// The declaration being referenced (the final identifier in the chain).
1123    ///
1124    /// This is always set and represents the identifier at the current point
1125    /// of reference (e.g., the variable name, import alias, or field name).
1126    pub decl: DeclExpr,
1127
1128    /// The intermediate expression in the resolution chain.
1129    ///
1130    /// Set in the following cases:
1131    /// - **Import/include**: The module expression being imported
1132    /// - **Field access**: The selected field's expression
1133    /// - **Scope resolution**: The scope expression being resolved
1134    /// - **Renamed imports**: The original name before renaming
1135    ///
1136    /// `None` when the identifier is an undefined reference.
1137    pub step: Option<Expr>,
1138
1139    /// The root expression at the start of the reference chain.
1140    ///
1141    /// A root definition never references another root definition.
1142    pub root: Option<Expr>,
1143
1144    /// The final resolved type of the referenced value.
1145    ///
1146    /// Set whenever a type is known for the referenced value.
1147    ///
1148    /// Some reference doesn't have a root definition, but has a term. For
1149    /// example, `std.length` is termed as `Type(length)` while has no a
1150    /// definition.
1151    pub term: Option<Ty>,
1152}
1153
1154/// Represents a content reference expression.
1155#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1156pub struct ContentRefExpr {
1157    /// The identifier being referenced.
1158    pub ident: DeclExpr,
1159    /// The declaration this reference points to (if resolved).
1160    pub of: Option<DeclExpr>,
1161    /// The body content associated with the reference.
1162    pub body: Option<Expr>,
1163}
1164
1165/// Represents a field selection expression.
1166#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1167pub struct SelectExpr {
1168    /// The left-hand side expression being selected from.
1169    pub lhs: Expr,
1170    /// The key or field name being selected.
1171    pub key: DeclExpr,
1172    /// The span location of this selection.
1173    pub span: Span,
1174}
1175
1176impl SelectExpr {
1177    /// Creates a new SelectExpr with the given key and left-hand side.
1178    pub fn new(key: DeclExpr, lhs: Expr) -> Interned<Self> {
1179        Interned::new(Self {
1180            key,
1181            lhs,
1182            span: Span::detached(),
1183        })
1184    }
1185}
1186
1187/// Represents an arguments expression.
1188#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1189pub struct ArgsExpr {
1190    /// The list of arguments.
1191    pub args: Vec<ArgExpr>,
1192    /// The span location of the argument list.
1193    pub span: Span,
1194}
1195
1196impl ArgsExpr {
1197    /// Creates a new ArgsExpr with the given span and arguments.
1198    pub fn new(span: Span, args: Vec<ArgExpr>) -> Interned<Self> {
1199        Interned::new(Self { args, span })
1200    }
1201}
1202
1203/// Represents an element expression.
1204#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1205pub struct ElementExpr {
1206    /// The Typst element type.
1207    pub elem: Element,
1208    /// The content expressions within this element.
1209    pub content: EcoVec<Expr>,
1210}
1211
1212/// Represents a function application expression.
1213#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1214pub struct ApplyExpr {
1215    /// The function expression being called.
1216    pub callee: Expr,
1217    /// The arguments passed to the function.
1218    pub args: Expr,
1219    /// The span location of the function call.
1220    pub span: Span,
1221}
1222
1223/// Represents a function expression.
1224#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1225pub struct FuncExpr {
1226    /// The declaration for this function.
1227    pub decl: DeclExpr,
1228    /// The parameter signature defining function inputs.
1229    pub params: PatternSig,
1230    /// The function body expression.
1231    pub body: Expr,
1232}
1233
1234/// Represents a let binding expression.
1235#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1236pub struct LetExpr {
1237    /// Span of the pattern.
1238    pub span: Span,
1239    /// The pattern being bound (left side of assignment).
1240    pub pattern: Interned<Pattern>,
1241    /// The optional body expression (right side of assignment).
1242    pub body: Option<Expr>,
1243}
1244
1245/// Represents a show rule expression.
1246#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1247pub struct ShowExpr {
1248    /// Optional selector expression to determine what to show.
1249    pub selector: Option<Expr>,
1250    /// The edit function to apply to selected elements.
1251    pub edit: Expr,
1252}
1253
1254/// Represents a set rule expression.
1255#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1256pub struct SetExpr {
1257    /// The target element or function to set.
1258    pub target: Expr,
1259    /// The arguments to apply to the target.
1260    pub args: Expr,
1261    /// Optional condition for when to apply the set rule.
1262    pub cond: Option<Expr>,
1263}
1264
1265/// Represents an import expression.
1266#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1267pub struct ImportExpr {
1268    /// The source expression indicating what file or module to import from.
1269    pub source: Expr,
1270    /// The reference expression for what is being imported.
1271    pub decl: Interned<RefExpr>,
1272}
1273
1274/// Represents an include expression.
1275#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1276pub struct IncludeExpr {
1277    /// The source expression indicating what file or content to include.
1278    pub source: Expr,
1279}
1280
1281/// Represents a conditional (if) expression.
1282#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1283pub struct IfExpr {
1284    /// The condition expression to evaluate.
1285    pub cond: Expr,
1286    /// The expression to evaluate if condition is true.
1287    pub then: Expr,
1288    /// The expression to evaluate if condition is false.
1289    pub else_: Expr,
1290}
1291
1292/// Represents a while loop expression.
1293#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1294pub struct WhileExpr {
1295    /// The condition expression evaluated each iteration.
1296    pub cond: Expr,
1297    /// The body expression executed while condition is true.
1298    pub body: Expr,
1299}
1300
1301/// Represents a for loop expression.
1302#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1303pub struct ForExpr {
1304    /// The pattern to match each iteration value against.
1305    pub pattern: Interned<Pattern>,
1306    /// The expression that produces values to iterate over.
1307    pub iter: Expr,
1308    /// The body expression executed for each iteration.
1309    pub body: Expr,
1310}
1311
1312/// The kind of unary operation.
1313#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
1314pub enum UnaryOp {
1315    /// The (arithmetic) positive operation.
1316    /// `+t`
1317    Pos,
1318    /// The (arithmetic) negate operation.
1319    /// `-t`
1320    Neg,
1321    /// The (logical) not operation.
1322    /// `not t`
1323    Not,
1324    /// The return operation.
1325    /// `return t`
1326    Return,
1327    /// The typst context operation.
1328    /// `context t`
1329    Context,
1330    /// The spreading operation.
1331    /// `..t`
1332    Spread,
1333    /// The not element of operation.
1334    /// `not in t`
1335    NotElementOf,
1336    /// The element of operation.
1337    /// `in t`
1338    ElementOf,
1339    /// The type of operation.
1340    /// `type(t)`
1341    TypeOf,
1342}
1343
1344/// A unary operation type.
1345#[derive(Debug, Hash, Clone, PartialEq, Eq)]
1346pub struct UnInst<T> {
1347    /// The operand of the unary operation.
1348    pub lhs: T,
1349    /// The kind of the unary operation.
1350    pub op: UnaryOp,
1351}
1352
1353impl<T: Ord> PartialOrd for UnInst<T> {
1354    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1355        Some(self.cmp(other))
1356    }
1357}
1358
1359impl<T: Ord> Ord for UnInst<T> {
1360    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1361        let op_as_int = self.op as u8;
1362        let other_op_as_int = other.op as u8;
1363        op_as_int
1364            .cmp(&other_op_as_int)
1365            .then_with(|| self.lhs.cmp(&other.lhs))
1366    }
1367}
1368
1369impl UnInst<Expr> {
1370    /// Creates a unary operation type with the given operator and operand.
1371    pub fn new(op: UnaryOp, lhs: Expr) -> Interned<Self> {
1372        Interned::new(Self { lhs, op })
1373    }
1374}
1375
1376impl<T> UnInst<T> {
1377    /// Gets the operands of the unary operation.
1378    pub fn operands(&self) -> [&T; 1] {
1379        [&self.lhs]
1380    }
1381}
1382
1383/// Type alias for binary operation types.
1384pub type BinaryOp = ast::BinOp;
1385
1386/// A binary operation type.
1387#[derive(Debug, Hash, Clone, PartialEq, Eq)]
1388pub struct BinInst<T> {
1389    /// The operands of the binary operation (left, right).
1390    pub operands: (T, T),
1391    /// The kind of the binary operation.
1392    pub op: BinaryOp,
1393}
1394
1395impl<T: Ord> PartialOrd for BinInst<T> {
1396    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1397        Some(self.cmp(other))
1398    }
1399}
1400
1401impl<T: Ord> Ord for BinInst<T> {
1402    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1403        let op_as_int = self.op as u8;
1404        let other_op_as_int = other.op as u8;
1405        op_as_int
1406            .cmp(&other_op_as_int)
1407            .then_with(|| self.operands.cmp(&other.operands))
1408    }
1409}
1410
1411impl BinInst<Expr> {
1412    /// Creates a binary operation type with the given operator and operands.
1413    pub fn new(op: BinaryOp, lhs: Expr, rhs: Expr) -> Interned<Self> {
1414        Interned::new(Self {
1415            operands: (lhs, rhs),
1416            op,
1417        })
1418    }
1419}
1420
1421impl<T> BinInst<T> {
1422    /// Gets the operands of the binary operation.
1423    pub fn operands(&self) -> [&T; 2] {
1424        [&self.operands.0, &self.operands.1]
1425    }
1426}
1427
1428/// Checks if a scope is empty.
1429fn is_empty_scope(scope: &typst::foundations::Scope) -> bool {
1430    scope.iter().next().is_none()
1431}
1432
1433impl_internable!(
1434    Expr,
1435    ArgsExpr,
1436    ElementExpr,
1437    ContentSeqExpr,
1438    RefExpr,
1439    ContentRefExpr,
1440    SelectExpr,
1441    ImportExpr,
1442    IncludeExpr,
1443    IfExpr,
1444    WhileExpr,
1445    ForExpr,
1446    FuncExpr,
1447    LetExpr,
1448    ShowExpr,
1449    SetExpr,
1450    Pattern,
1451    EcoVec<(Decl, Expr)>,
1452    Vec<ArgExpr>,
1453    Vec<Expr>,
1454    UnInst<Expr>,
1455    BinInst<Expr>,
1456    ApplyExpr,
1457);
1458
1459#[cfg(test)]
1460mod tests {
1461    use std::cmp::Ordering;
1462    use std::str::FromStr;
1463
1464    use typst::syntax::{FileId, RootedPath, VirtualPath, VirtualRoot};
1465
1466    use super::{Decl, StrictCmp};
1467    use crate::adt::interner::Interned;
1468    use crate::prelude::TypstFileId;
1469    use crate::ty::TypeVar;
1470
1471    fn package(spec: &str) -> typst::syntax::package::PackageSpec {
1472        typst::syntax::package::PackageSpec::from_str(spec).expect("valid package spec")
1473    }
1474
1475    fn rooted_path(root: VirtualRoot, path: &str) -> RootedPath {
1476        RootedPath::new(root, VirtualPath::new(path).expect("valid virtual path"))
1477    }
1478
1479    fn file_id(root: VirtualRoot, path: &str) -> TypstFileId {
1480        FileId::new(rooted_path(root, path))
1481    }
1482
1483    fn unique_file_id(root: VirtualRoot, path: &str) -> TypstFileId {
1484        FileId::unique(rooted_path(root, path))
1485    }
1486
1487    #[test]
1488    fn strict_file_id_cmp_eq_for_same_project_path() {
1489        let left = file_id(VirtualRoot::Project, "/main.typ");
1490        let right = file_id(VirtualRoot::Project, "/main.typ");
1491
1492        assert_eq!(left, right);
1493        assert_eq!(left.strict_cmp(&right), Ordering::Equal);
1494        assert_eq!(
1495            Decl::module(left).strict_cmp(&Decl::module(right)),
1496            Ordering::Equal
1497        );
1498    }
1499
1500    #[test]
1501    fn strict_file_id_cmp_eq_for_same_package_and_path() {
1502        let root = VirtualRoot::Package(package("@preview/example:0.1.0"));
1503        let left = file_id(root.clone(), "/lib.typ");
1504        let right = file_id(root, "/lib.typ");
1505
1506        assert_eq!(left, right);
1507        assert_eq!(left.strict_cmp(&right), Ordering::Equal);
1508        assert_eq!(
1509            Decl::module(left).strict_cmp(&Decl::module(right)),
1510            Ordering::Equal
1511        );
1512    }
1513
1514    #[test]
1515    fn strict_file_id_cmp_ignores_unique_raw_id_for_same_root_and_path() {
1516        let root = VirtualRoot::Package(package("@preview/example:0.1.0"));
1517        let left = unique_file_id(root.clone(), "/lib.typ");
1518        let right = unique_file_id(root, "/lib.typ");
1519
1520        assert_ne!(left.into_raw(), right.into_raw());
1521        assert_eq!(left.root(), right.root());
1522        assert_eq!(
1523            left.vpath().get_with_slash(),
1524            right.vpath().get_with_slash()
1525        );
1526        assert_eq!(left.strict_cmp(&right), Ordering::Equal);
1527        assert_eq!(
1528            Decl::module(left).strict_cmp(&Decl::module(right)),
1529            Ordering::Equal
1530        );
1531    }
1532
1533    #[test]
1534    fn strict_file_id_cmp_distinguishes_package_or_path() {
1535        let package_root = VirtualRoot::Package(package("@preview/example:0.1.0"));
1536        let same_path = "/lib.typ";
1537        let project_file = file_id(VirtualRoot::Project, same_path);
1538        let package_file = file_id(package_root.clone(), same_path);
1539        let other_package_file = file_id(
1540            VirtualRoot::Package(package("@preview/other:0.1.0")),
1541            same_path,
1542        );
1543        let other_path = file_id(package_root, "/other.typ");
1544
1545        assert_ne!(project_file.strict_cmp(&package_file), Ordering::Equal);
1546        assert_ne!(
1547            package_file.strict_cmp(&other_package_file),
1548            Ordering::Equal
1549        );
1550        assert_ne!(package_file.strict_cmp(&other_path), Ordering::Equal);
1551    }
1552
1553    #[test]
1554    fn docs_decl_inherits_base_file_id() {
1555        let fid = file_id(VirtualRoot::Project, "/main.typ");
1556        let base: Interned<Decl> = Decl::module(fid).into();
1557        let var = TypeVar::new("input".into(), base.clone());
1558        let docs = Decl::docs(base, var);
1559
1560        assert_eq!(docs.file_id(), Some(fid));
1561    }
1562}