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        // Raw file-id and span identities are assigned in interning order,
753        // which depends on thread scheduling. Every ordered collection and
754        // sort over declarations (and types containing them) must therefore
755        // use the content-stable comparison, or inferred results change
756        // between runs.
757        self.strict_cmp(other)
758    }
759}
760
761pub(crate) trait StrictCmp {
762    /// Compares by stable content instead of raw interned identity, which is
763    /// assigned in interning order and therefore depends on thread
764    /// scheduling.
765    fn strict_cmp(&self, other: &Self) -> std::cmp::Ordering;
766}
767
768impl Decl {
769    /// Low-performance comparison that is free from concurrency issues.
770    pub fn strict_cmp(&self, other: &Self) -> std::cmp::Ordering {
771        let base = match (self, other) {
772            (Self::Generated(l), Self::Generated(r)) => l.0.0.cmp(&r.0.0),
773            (Self::Module(l), Self::Module(r)) => l.fid.strict_cmp(&r.fid),
774            (Self::Docs(l), Self::Docs(r)) => l
775                .var
776                .strict_cmp(&r.var)
777                .then_with(|| l.base.strict_cmp(&r.base)),
778            _ => self.span().strict_cmp(&other.span()),
779        };
780
781        base.then_with(|| self.name().cmp(other.name()))
782    }
783}
784
785impl StrictCmp for TypstFileId {
786    fn strict_cmp(&self, other: &Self) -> std::cmp::Ordering {
787        // Identical ids need no path comparison; this keeps the common
788        // same-file case cheap now that the default `Decl` ordering is
789        // content-stable.
790        if self == other {
791            return std::cmp::Ordering::Equal;
792        }
793
794        fn root_cmp(left: &VirtualRoot, right: &VirtualRoot) -> std::cmp::Ordering {
795            match (left, right) {
796                (VirtualRoot::Project, VirtualRoot::Project) => std::cmp::Ordering::Equal,
797                (VirtualRoot::Project, VirtualRoot::Package(_)) => std::cmp::Ordering::Less,
798                (VirtualRoot::Package(_), VirtualRoot::Project) => std::cmp::Ordering::Greater,
799                (VirtualRoot::Package(left), VirtualRoot::Package(right)) => left
800                    .namespace
801                    .cmp(&right.namespace)
802                    .then_with(|| left.name.cmp(&right.name))
803                    .then_with(|| left.version.cmp(&right.version)),
804            }
805        }
806
807        root_cmp(self.root(), other.root()).then_with(|| {
808            self.vpath()
809                .get_with_slash()
810                .cmp(other.vpath().get_with_slash())
811        })
812    }
813}
814impl<T: StrictCmp> StrictCmp for Option<T> {
815    fn strict_cmp(&self, other: &Self) -> std::cmp::Ordering {
816        match (self, other) {
817            (Some(l), Some(r)) => l.strict_cmp(r),
818            (Some(_), None) => std::cmp::Ordering::Greater,
819            (None, Some(_)) => std::cmp::Ordering::Less,
820            (None, None) => std::cmp::Ordering::Equal,
821        }
822    }
823}
824
825impl StrictCmp for Span {
826    fn strict_cmp(&self, other: &Self) -> std::cmp::Ordering {
827        self.id()
828            .strict_cmp(&other.id())
829            .then_with(|| self.into_raw().cmp(&other.into_raw()))
830    }
831}
832
833impl PartialOrd for Decl {
834    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
835        Some(self.cmp(other))
836    }
837}
838
839impl From<Decl> for Expr {
840    fn from(decl: Decl) -> Self {
841        Expr::Decl(decl.into())
842    }
843}
844
845impl From<DeclExpr> for Expr {
846    fn from(decl: DeclExpr) -> Self {
847        Expr::Decl(decl)
848    }
849}
850
851/// A declaration with an associated name and span location.
852#[derive(Clone, PartialEq, Eq, Hash)]
853pub struct SpannedDecl {
854    /// The name of the declaration.
855    name: Interned<str>,
856    /// The span location of the declaration.
857    at: Span,
858}
859
860impl SpannedDecl {
861    /// Gets the name of the declaration.
862    fn name(&self) -> &Interned<str> {
863        &self.name
864    }
865
866    /// Gets the span location of the declaration.
867    fn span(&self) -> Span {
868        self.at
869    }
870}
871
872impl fmt::Debug for SpannedDecl {
873    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
874        f.write_str(self.name.as_ref())
875    }
876}
877
878/// A declaration with a name and range information.
879#[derive(Clone, PartialEq, Eq, Hash)]
880pub struct NameRangeDecl {
881    /// The name of the declaration.
882    pub name: Interned<str>,
883    /// Boxed tuple containing (file_id, name_range, full_range).
884    pub at: Box<(TypstFileId, Range<usize>, Option<Range<usize>>)>,
885}
886
887impl NameRangeDecl {
888    /// Gets the name of the declaration.
889    fn name(&self) -> &Interned<str> {
890        &self.name
891    }
892
893    /// Gets the span location of the declaration.
894    fn span(&self) -> Span {
895        Span::detached()
896    }
897}
898
899impl fmt::Debug for NameRangeDecl {
900    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
901        f.write_str(self.name.as_ref())
902    }
903}
904
905/// A module declaration with name and file ID.
906#[derive(Clone, PartialEq, Eq, Hash)]
907pub struct ModuleDecl {
908    /// The name of the module.
909    pub name: Interned<str>,
910    /// The file ID where the module is defined.
911    pub fid: TypstFileId,
912}
913
914impl ModuleDecl {
915    /// Gets the name of the declaration.
916    fn name(&self) -> &Interned<str> {
917        &self.name
918    }
919
920    /// Gets the span location of the declaration.
921    fn span(&self) -> Span {
922        Span::detached()
923    }
924}
925
926impl fmt::Debug for ModuleDecl {
927    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
928        f.write_str(self.name.as_ref())
929    }
930}
931
932/// A documentation declaration linking a base declaration with type variables.
933#[derive(Clone, PartialEq, Eq, Hash)]
934pub struct DocsDecl {
935    base: Interned<Decl>,
936    var: Interned<TypeVar>,
937}
938
939impl DocsDecl {
940    /// Gets the name of the declaration.
941    fn name(&self) -> &Interned<str> {
942        Interned::empty()
943    }
944
945    /// Gets the span location of the declaration.
946    fn span(&self) -> Span {
947        Span::detached()
948    }
949}
950
951impl fmt::Debug for DocsDecl {
952    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
953        write!(f, "{:?}, {:?}", self.base, self.var)
954    }
955}
956
957/// A span-only declaration for anonymous constructs.
958#[derive(Clone, PartialEq, Eq, Hash)]
959pub struct SpanDecl(Span);
960
961impl SpanDecl {
962    /// Gets the name of the declaration.
963    fn name(&self) -> &Interned<str> {
964        Interned::empty()
965    }
966
967    /// Gets the span location of the declaration.
968    fn span(&self) -> Span {
969        self.0
970    }
971}
972
973impl fmt::Debug for SpanDecl {
974    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
975        write!(f, "..")
976    }
977}
978
979/// A generated declaration with a unique definition ID.
980#[derive(Clone, PartialEq, Eq, Hash)]
981pub struct GeneratedDecl(DefId);
982
983impl GeneratedDecl {
984    /// Gets the name of the declaration.
985    fn name(&self) -> &Interned<str> {
986        Interned::empty()
987    }
988
989    /// Gets the span location of the declaration.
990    fn span(&self) -> Span {
991        Span::detached()
992    }
993}
994
995impl fmt::Debug for GeneratedDecl {
996    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
997        self.0.fmt(f)
998    }
999}
1000
1001/// Type alias for unary expressions.
1002pub type UnExpr = UnInst<Expr>;
1003/// Type alias for binary expressions.
1004pub type BinExpr = BinInst<Expr>;
1005
1006/// Type alias for export maps.
1007///
1008/// Maps exported names to their corresponding expressions.
1009pub type ExportMap = BTreeMap<Interned<str>, Expr>;
1010
1011/// Represents different kinds of function arguments.
1012///
1013/// Covers positional arguments, named arguments, and spread arguments.
1014#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1015pub enum ArgExpr {
1016    /// A positional argument: `x`
1017    Pos(Expr),
1018    /// A named argument: `a: x`
1019    Named(Box<(DeclExpr, Expr)>),
1020    /// A named argument with a default value: `((a): x)`
1021    NamedRt(Box<(Expr, Expr)>),
1022    /// A spread argument: `..x`
1023    Spread(Expr),
1024}
1025
1026/// Represents different kinds of patterns for destructuring.
1027#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1028pub enum Pattern {
1029    /// A general pattern expression can occur in right-hand side of a
1030    /// function signature.
1031    Expr(Expr),
1032    /// A simple pattern: `x`
1033    Simple(Interned<Decl>),
1034    /// A pattern signature: `(x, y: val, ..z)`
1035    Sig(Box<PatternSig>),
1036}
1037
1038impl fmt::Display for Pattern {
1039    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1040        ExprPrinter::new(f).write_pattern(self)
1041    }
1042}
1043
1044impl Pattern {
1045    /// Returns a string representation of the pattern.
1046    pub fn repr(&self) -> EcoString {
1047        let mut s = EcoString::new();
1048        let _ = ExprDescriber::new(&mut s).write_pattern(self);
1049        s
1050    }
1051}
1052
1053/// Signature pattern for function parameters.
1054///
1055/// Describes the structure of function parameters including positional,
1056/// named, and spread parameters.
1057#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1058pub struct PatternSig {
1059    /// Positional parameters in order.
1060    pub pos: EcoVec<Interned<Pattern>>,
1061    /// Named parameters with their default patterns.
1062    pub named: EcoVec<(DeclExpr, Interned<Pattern>)>,
1063    /// Left spread parameter (collects extra positional arguments).
1064    pub spread_left: Option<(DeclExpr, Interned<Pattern>)>,
1065    /// Right spread parameter (collects remaining arguments).
1066    pub spread_right: Option<(DeclExpr, Interned<Pattern>)>,
1067}
1068
1069impl Pattern {}
1070
1071impl_internable!(Decl,);
1072
1073/// Represents a content sequence expression.
1074///
1075/// Used for sequences of content elements with associated type information.
1076#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1077pub struct ContentSeqExpr {
1078    /// The type of the content sequence
1079    pub ty: Ty,
1080}
1081
1082/// Represents a reference expression.
1083///
1084/// A reference expression tracks how an identifier resolves through the lexical
1085/// scope, imports, and field accesses. It maintains a chain of resolution steps
1086/// to support features like go-to-definition, go-to-reference, and type
1087/// inference.
1088///
1089/// # Resolution Chain
1090///
1091/// The fields form a resolution chain: `root` -> `step` -> `decl`, where:
1092/// - `root` is the original source of the value
1093/// - `step` is any intermediate transformation
1094/// - `decl` is the final identifier being referenced
1095/// - `term` is the resolved type (if known)
1096///   - Hint: A value `1`'s typst type is `int`, but here we keep the type as
1097///     `1` to improve the type inference.
1098///
1099/// # Examples
1100///
1101/// ## Simple identifier reference
1102/// ```rust,ignore
1103/// // For: let x = value; let y = x;
1104/// RefExpr {
1105///     decl: y,           // The identifier 'y'
1106///     root: Some(x),     // Points back to 'x'
1107///     step: Some(x),     // Same as root for simple refs
1108///     term: None,        // Type may not be known yet
1109/// }
1110/// ```
1111///
1112/// ## Import with rename
1113/// ```rust,ignore
1114/// // For: import "mod.typ": old as new
1115/// // First creates ref for 'old':
1116/// RefExpr { decl: old, root: Some(mod.old), step: Some(field), term: Some(Func(() -> dict)) }
1117/// // Then creates ref for 'new':
1118/// RefExpr { decl: new, root: Some(mod.old), step: Some(old), term: Some(Func(() -> dict)) }
1119/// ```
1120///
1121/// ## Builtin definitions
1122/// ```rust,ignore
1123/// // For: std.length
1124/// RefExpr { decl: length, root: None, step: None, term: Some(Type(length)) }
1125/// ```
1126#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1127pub struct RefExpr {
1128    /// The declaration being referenced (the final identifier in the chain).
1129    ///
1130    /// This is always set and represents the identifier at the current point
1131    /// of reference (e.g., the variable name, import alias, or field name).
1132    pub decl: DeclExpr,
1133
1134    /// The intermediate expression in the resolution chain.
1135    ///
1136    /// Set in the following cases:
1137    /// - **Import/include**: The module expression being imported
1138    /// - **Field access**: The selected field's expression
1139    /// - **Scope resolution**: The scope expression being resolved
1140    /// - **Renamed imports**: The original name before renaming
1141    ///
1142    /// `None` when the identifier is an undefined reference.
1143    pub step: Option<Expr>,
1144
1145    /// The root expression at the start of the reference chain.
1146    ///
1147    /// A root definition never references another root definition.
1148    pub root: Option<Expr>,
1149
1150    /// The final resolved type of the referenced value.
1151    ///
1152    /// Set whenever a type is known for the referenced value.
1153    ///
1154    /// Some reference doesn't have a root definition, but has a term. For
1155    /// example, `std.length` is termed as `Type(length)` while has no a
1156    /// definition.
1157    pub term: Option<Ty>,
1158}
1159
1160/// Represents a content reference expression.
1161#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1162pub struct ContentRefExpr {
1163    /// The identifier being referenced.
1164    pub ident: DeclExpr,
1165    /// The declaration this reference points to (if resolved).
1166    pub of: Option<DeclExpr>,
1167    /// The body content associated with the reference.
1168    pub body: Option<Expr>,
1169}
1170
1171/// Represents a field selection expression.
1172#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1173pub struct SelectExpr {
1174    /// The left-hand side expression being selected from.
1175    pub lhs: Expr,
1176    /// The key or field name being selected.
1177    pub key: DeclExpr,
1178    /// The span location of this selection.
1179    pub span: Span,
1180}
1181
1182impl SelectExpr {
1183    /// Creates a new SelectExpr with the given key and left-hand side.
1184    pub fn new(key: DeclExpr, lhs: Expr) -> Interned<Self> {
1185        Interned::new(Self {
1186            key,
1187            lhs,
1188            span: Span::detached(),
1189        })
1190    }
1191}
1192
1193/// Represents an arguments expression.
1194#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1195pub struct ArgsExpr {
1196    /// The list of arguments.
1197    pub args: Vec<ArgExpr>,
1198    /// The span location of the argument list.
1199    pub span: Span,
1200}
1201
1202impl ArgsExpr {
1203    /// Creates a new ArgsExpr with the given span and arguments.
1204    pub fn new(span: Span, args: Vec<ArgExpr>) -> Interned<Self> {
1205        Interned::new(Self { args, span })
1206    }
1207}
1208
1209/// Represents an element expression.
1210#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1211pub struct ElementExpr {
1212    /// The Typst element type.
1213    pub elem: Element,
1214    /// The content expressions within this element.
1215    pub content: EcoVec<Expr>,
1216}
1217
1218/// Represents a function application expression.
1219#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1220pub struct ApplyExpr {
1221    /// The function expression being called.
1222    pub callee: Expr,
1223    /// The arguments passed to the function.
1224    pub args: Expr,
1225    /// The span location of the function call.
1226    pub span: Span,
1227}
1228
1229/// Represents a function expression.
1230#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1231pub struct FuncExpr {
1232    /// The declaration for this function.
1233    pub decl: DeclExpr,
1234    /// The parameter signature defining function inputs.
1235    pub params: PatternSig,
1236    /// The function body expression.
1237    pub body: Expr,
1238}
1239
1240/// Represents a let binding expression.
1241#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1242pub struct LetExpr {
1243    /// Span of the pattern.
1244    pub span: Span,
1245    /// The pattern being bound (left side of assignment).
1246    pub pattern: Interned<Pattern>,
1247    /// The optional body expression (right side of assignment).
1248    pub body: Option<Expr>,
1249}
1250
1251/// Represents a show rule expression.
1252#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1253pub struct ShowExpr {
1254    /// Optional selector expression to determine what to show.
1255    pub selector: Option<Expr>,
1256    /// The edit function to apply to selected elements.
1257    pub edit: Expr,
1258}
1259
1260/// Represents a set rule expression.
1261#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1262pub struct SetExpr {
1263    /// The target element or function to set.
1264    pub target: Expr,
1265    /// The arguments to apply to the target.
1266    pub args: Expr,
1267    /// Optional condition for when to apply the set rule.
1268    pub cond: Option<Expr>,
1269}
1270
1271/// Represents an import expression.
1272#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1273pub struct ImportExpr {
1274    /// The source expression indicating what file or module to import from.
1275    pub source: Expr,
1276    /// The reference expression for what is being imported.
1277    pub decl: Interned<RefExpr>,
1278}
1279
1280/// Represents an include expression.
1281#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1282pub struct IncludeExpr {
1283    /// The source expression indicating what file or content to include.
1284    pub source: Expr,
1285}
1286
1287/// Represents a conditional (if) expression.
1288#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1289pub struct IfExpr {
1290    /// The condition expression to evaluate.
1291    pub cond: Expr,
1292    /// The expression to evaluate if condition is true.
1293    pub then: Expr,
1294    /// The expression to evaluate if condition is false.
1295    pub else_: Expr,
1296}
1297
1298/// Represents a while loop expression.
1299#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1300pub struct WhileExpr {
1301    /// The condition expression evaluated each iteration.
1302    pub cond: Expr,
1303    /// The body expression executed while condition is true.
1304    pub body: Expr,
1305}
1306
1307/// Represents a for loop expression.
1308#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1309pub struct ForExpr {
1310    /// The pattern to match each iteration value against.
1311    pub pattern: Interned<Pattern>,
1312    /// The expression that produces values to iterate over.
1313    pub iter: Expr,
1314    /// The body expression executed for each iteration.
1315    pub body: Expr,
1316}
1317
1318/// The kind of unary operation.
1319#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
1320pub enum UnaryOp {
1321    /// The (arithmetic) positive operation.
1322    /// `+t`
1323    Pos,
1324    /// The (arithmetic) negate operation.
1325    /// `-t`
1326    Neg,
1327    /// The (logical) not operation.
1328    /// `not t`
1329    Not,
1330    /// The return operation.
1331    /// `return t`
1332    Return,
1333    /// The typst context operation.
1334    /// `context t`
1335    Context,
1336    /// The spreading operation.
1337    /// `..t`
1338    Spread,
1339    /// The not element of operation.
1340    /// `not in t`
1341    NotElementOf,
1342    /// The element of operation.
1343    /// `in t`
1344    ElementOf,
1345    /// The type of operation.
1346    /// `type(t)`
1347    TypeOf,
1348}
1349
1350/// A unary operation type.
1351#[derive(Debug, Hash, Clone, PartialEq, Eq)]
1352pub struct UnInst<T> {
1353    /// The operand of the unary operation.
1354    pub lhs: T,
1355    /// The kind of the unary operation.
1356    pub op: UnaryOp,
1357}
1358
1359impl<T: Ord> PartialOrd for UnInst<T> {
1360    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1361        Some(self.cmp(other))
1362    }
1363}
1364
1365impl<T: Ord> Ord for UnInst<T> {
1366    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1367        let op_as_int = self.op as u8;
1368        let other_op_as_int = other.op as u8;
1369        op_as_int
1370            .cmp(&other_op_as_int)
1371            .then_with(|| self.lhs.cmp(&other.lhs))
1372    }
1373}
1374
1375impl UnInst<Expr> {
1376    /// Creates a unary operation type with the given operator and operand.
1377    pub fn new(op: UnaryOp, lhs: Expr) -> Interned<Self> {
1378        Interned::new(Self { lhs, op })
1379    }
1380}
1381
1382impl<T> UnInst<T> {
1383    /// Gets the operands of the unary operation.
1384    pub fn operands(&self) -> [&T; 1] {
1385        [&self.lhs]
1386    }
1387}
1388
1389/// Type alias for binary operation types.
1390pub type BinaryOp = ast::BinOp;
1391
1392/// A binary operation type.
1393#[derive(Debug, Hash, Clone, PartialEq, Eq)]
1394pub struct BinInst<T> {
1395    /// The operands of the binary operation (left, right).
1396    pub operands: (T, T),
1397    /// The kind of the binary operation.
1398    pub op: BinaryOp,
1399}
1400
1401impl<T: Ord> PartialOrd for BinInst<T> {
1402    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1403        Some(self.cmp(other))
1404    }
1405}
1406
1407impl<T: Ord> Ord for BinInst<T> {
1408    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1409        let op_as_int = self.op as u8;
1410        let other_op_as_int = other.op as u8;
1411        op_as_int
1412            .cmp(&other_op_as_int)
1413            .then_with(|| self.operands.cmp(&other.operands))
1414    }
1415}
1416
1417impl BinInst<Expr> {
1418    /// Creates a binary operation type with the given operator and operands.
1419    pub fn new(op: BinaryOp, lhs: Expr, rhs: Expr) -> Interned<Self> {
1420        Interned::new(Self {
1421            operands: (lhs, rhs),
1422            op,
1423        })
1424    }
1425}
1426
1427impl<T> BinInst<T> {
1428    /// Gets the operands of the binary operation.
1429    pub fn operands(&self) -> [&T; 2] {
1430        [&self.operands.0, &self.operands.1]
1431    }
1432}
1433
1434/// Checks if a scope is empty.
1435fn is_empty_scope(scope: &typst::foundations::Scope) -> bool {
1436    scope.iter().next().is_none()
1437}
1438
1439impl_internable!(
1440    Expr,
1441    ArgsExpr,
1442    ElementExpr,
1443    ContentSeqExpr,
1444    RefExpr,
1445    ContentRefExpr,
1446    SelectExpr,
1447    ImportExpr,
1448    IncludeExpr,
1449    IfExpr,
1450    WhileExpr,
1451    ForExpr,
1452    FuncExpr,
1453    LetExpr,
1454    ShowExpr,
1455    SetExpr,
1456    Pattern,
1457    EcoVec<(Decl, Expr)>,
1458    Vec<ArgExpr>,
1459    Vec<Expr>,
1460    UnInst<Expr>,
1461    BinInst<Expr>,
1462    ApplyExpr,
1463);
1464
1465#[cfg(test)]
1466mod tests {
1467    use std::cmp::Ordering;
1468    use std::str::FromStr;
1469
1470    use typst::syntax::{FileId, RootedPath, VirtualPath, VirtualRoot};
1471
1472    use super::{Decl, StrictCmp};
1473    use crate::adt::interner::Interned;
1474    use crate::prelude::TypstFileId;
1475    use crate::ty::TypeVar;
1476
1477    fn package(spec: &str) -> typst::syntax::package::PackageSpec {
1478        typst::syntax::package::PackageSpec::from_str(spec).expect("valid package spec")
1479    }
1480
1481    fn rooted_path(root: VirtualRoot, path: &str) -> RootedPath {
1482        RootedPath::new(root, VirtualPath::new(path).expect("valid virtual path"))
1483    }
1484
1485    fn file_id(root: VirtualRoot, path: &str) -> TypstFileId {
1486        FileId::new(rooted_path(root, path))
1487    }
1488
1489    fn unique_file_id(root: VirtualRoot, path: &str) -> TypstFileId {
1490        FileId::unique(rooted_path(root, path))
1491    }
1492
1493    #[test]
1494    fn strict_file_id_cmp_eq_for_same_project_path() {
1495        let left = file_id(VirtualRoot::Project, "/main.typ");
1496        let right = file_id(VirtualRoot::Project, "/main.typ");
1497
1498        assert_eq!(left, right);
1499        assert_eq!(left.strict_cmp(&right), Ordering::Equal);
1500        assert_eq!(
1501            Decl::module(left).strict_cmp(&Decl::module(right)),
1502            Ordering::Equal
1503        );
1504    }
1505
1506    #[test]
1507    fn strict_file_id_cmp_eq_for_same_package_and_path() {
1508        let root = VirtualRoot::Package(package("@preview/example:0.1.0"));
1509        let left = file_id(root.clone(), "/lib.typ");
1510        let right = file_id(root, "/lib.typ");
1511
1512        assert_eq!(left, right);
1513        assert_eq!(left.strict_cmp(&right), Ordering::Equal);
1514        assert_eq!(
1515            Decl::module(left).strict_cmp(&Decl::module(right)),
1516            Ordering::Equal
1517        );
1518    }
1519
1520    #[test]
1521    fn strict_file_id_cmp_ignores_unique_raw_id_for_same_root_and_path() {
1522        let root = VirtualRoot::Package(package("@preview/example:0.1.0"));
1523        let left = unique_file_id(root.clone(), "/lib.typ");
1524        let right = unique_file_id(root, "/lib.typ");
1525
1526        assert_ne!(left.into_raw(), right.into_raw());
1527        assert_eq!(left.root(), right.root());
1528        assert_eq!(
1529            left.vpath().get_with_slash(),
1530            right.vpath().get_with_slash()
1531        );
1532        assert_eq!(left.strict_cmp(&right), Ordering::Equal);
1533        assert_eq!(
1534            Decl::module(left).strict_cmp(&Decl::module(right)),
1535            Ordering::Equal
1536        );
1537    }
1538
1539    #[test]
1540    fn strict_file_id_cmp_distinguishes_package_or_path() {
1541        let package_root = VirtualRoot::Package(package("@preview/example:0.1.0"));
1542        let same_path = "/lib.typ";
1543        let project_file = file_id(VirtualRoot::Project, same_path);
1544        let package_file = file_id(package_root.clone(), same_path);
1545        let other_package_file = file_id(
1546            VirtualRoot::Package(package("@preview/other:0.1.0")),
1547            same_path,
1548        );
1549        let other_path = file_id(package_root, "/other.typ");
1550
1551        assert_ne!(project_file.strict_cmp(&package_file), Ordering::Equal);
1552        assert_ne!(
1553            package_file.strict_cmp(&other_package_file),
1554            Ordering::Equal
1555        );
1556        assert_ne!(package_file.strict_cmp(&other_path), Ordering::Equal);
1557    }
1558
1559    #[test]
1560    fn docs_decl_inherits_base_file_id() {
1561        let fid = file_id(VirtualRoot::Project, "/main.typ");
1562        let base: Interned<Decl> = Decl::module(fid).into();
1563        let var = TypeVar::new("input".into(), base.clone());
1564        let docs = Decl::docs(base, var);
1565
1566        assert_eq!(docs.file_id(), Some(fid));
1567    }
1568}