Skip to main content

Crate module_lang

Crate module_lang 

Source
Expand description

§module_lang

Module and import resolution across multiple source files. Given the modules a front-end has already parsed — each exporting a set of named items — this crate answers the question every use/import asks: which item does this name refer to?

A ModuleGraph holds one module per source file, each carrying the SourceId it was read from and a flat namespace of names. A name is either defined in the module, with a Visibility and a language-supplied payload, or imported into it from another module. resolve walks definitions and import edges to the payload a name refers to, or returns a defined ResolveError for a name that is missing, private, or part of an import cycle.

It sits in the SEMA tier of the -lang family, above symbol_lang — which interns the names this crate keys on — and source_lang — which owns the files the modules came from. It owns module and import resolution only: no parsing, no type checking, no code generation.

§Model

Modules are added in order and identified by a stable ModuleId. Each module’s names live in a BTreeMap keyed on the interned Symbol, so a lookup compares integers in O(log items) and iteration is deterministic. A name is declared at most once per module — a second definition or a colliding import is a ResolveError::DuplicateName.

Resolution treats a module’s own names as fully visible: a name defined in a module resolves from within it whether it is public or private. Crossing a module boundary through an import is where Visibility applies — an import reaches a name in another module only if it is Visibility::Public. Imports chain (a module may re-export what it imported), and the walk tracks the (module, name) pairs it has seen, so a chain that loops back is reported as a ResolveError::ImportCycle in bounded time rather than recursing without end.

§Quickstart

use intern_lang::Interner;
use module_lang::{ModuleGraph, ResolveError, Visibility};
use source_lang::SourceMap;

// Two files, each a module: `app` uses an item exported by `util`.
let mut sources = SourceMap::new();
let app_src = sources.add("app.lang", "use util.helper;")?;
let util_src = sources.add("util.lang", "pub fn helper() {}")?;

let mut names = Interner::new();
let helper = names.intern("helper");

let mut graph: ModuleGraph<&str> = ModuleGraph::new();
let app = graph.add_module(names.intern("app"), app_src);
let util = graph.add_module(names.intern("util"), util_src);

graph.define(util, helper, Visibility::Public, "fn helper")?;
graph.import(app, util, helper)?;

// The import resolves through to the definition in `util`.
assert_eq!(graph.resolve(app, helper), Ok(&"fn helper"));

// A name nobody declared is a defined error, never a panic.
let missing = names.intern("nope");
assert!(matches!(graph.resolve(app, missing), Err(ResolveError::Unresolved { .. })));

§Stability

The public API is stable as of 1.0 and follows Semantic Versioning: no breaking changes before 2.0, additions arrive in minor releases, and the MSRV (Rust 1.85) only rises in a minor. ResolveError is #[non_exhaustive], so a new variant is a minor change. The full promise is in docs/API.md.

Structs§

ModuleGraph
A set of modules and the import edges between them, against which names are resolved.
ModuleId
A small, copyable handle to one module in a ModuleGraph.
SourceId
A small, copyable handle to one source in a SourceMap.
Symbol
A small, copyable handle to a string held by an Interner.

Enums§

ResolveError
The reason a graph operation or a name resolution did not succeed.
Visibility
Whether an item is visible outside the module that declares it.