Iter 13d: rustdoc polish for ailang-core + new docwriter agent
Adds ailang-docwriter to /agents/ — a recurring role for keeping crate-, module-, and pub-item-level rustdoc accurate. First mission: ailang-core. Crate root, every module root, every pub item documented; intra-doc links throughout; Iter-13a additions (TypeDef.vars, Type::Con.args) get an explicit backwards-compat note. Two stale broken-link warnings in ailang-check fixed in passing. cargo doc --no-deps now warning-free across the workspace; promoted to verification invariant 6 in DESIGN.md.
This commit is contained in:
@@ -1,10 +1,35 @@
|
||||
//! Workspace loader: loads an entry module and recursively follows its
|
||||
//! `imports`. Convention: an `import { module: "foo" }` is resolved
|
||||
//! relative to the entry file's directory as `<root_dir>/foo.ail.json`.
|
||||
//! `imports`.
|
||||
//!
|
||||
//! Iter 5a is responsible for **finding** and consistently **loading**
|
||||
//! all reachable modules. Cross-module typecheck (5b) and codegen (5c)
|
||||
//! build on top, but are not part of this step.
|
||||
//! Convention: an `import { module: "foo" }` is resolved relative to
|
||||
//! the entry file's directory as `<root_dir>/foo.ail.json`. Module
|
||||
//! names must match the file stem (the loader rejects mismatches).
|
||||
//!
|
||||
//! The single entry point is [`load_workspace`]; it returns a fully
|
||||
//! populated [`Workspace`] or a structured [`WorkspaceLoadError`].
|
||||
//! [`module_hash`] is the module-granularity counterpart to
|
||||
//! [`crate::def_hash`] and is used both internally (to detect a module
|
||||
//! re-loaded from disk with different content) and by the CLI.
|
||||
//!
|
||||
//! This module is responsible only for **finding** and consistently
|
||||
//! **loading** all reachable modules. Cross-module typechecking lives
|
||||
//! in `ailang-check`; codegen in `ailang-codegen`. Neither is run from
|
||||
//! here.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```ignore
|
||||
//! use ailang_core::load_workspace;
|
||||
//! use std::path::Path;
|
||||
//!
|
||||
//! let ws = load_workspace(Path::new("examples/ws_main.ail.json"))?;
|
||||
//! assert_eq!(ws.entry, "ws_main");
|
||||
//! // All transitively imported modules are now in `ws.modules`.
|
||||
//! for (name, m) in &ws.modules {
|
||||
//! println!("{name}: {} defs", m.defs.len());
|
||||
//! }
|
||||
//! # Ok::<(), ailang_core::workspace::WorkspaceLoadError>(())
|
||||
//! ```
|
||||
|
||||
use crate::ast::Module;
|
||||
use crate::canonical;
|
||||
@@ -20,8 +45,14 @@ use std::path::{Path, PathBuf};
|
||||
/// in; all imports are resolved relative to it.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Workspace {
|
||||
/// Name of the entry module (the one passed to [`load_workspace`]).
|
||||
pub entry: String,
|
||||
/// Every module reachable from `entry`, indexed by module name.
|
||||
/// `BTreeMap` is used so iteration order is deterministic, which
|
||||
/// matters for downstream codegen and reporting.
|
||||
pub modules: BTreeMap<String, Module>,
|
||||
/// Directory the entry file lives in; all imports are resolved
|
||||
/// relative to it.
|
||||
pub root_dir: PathBuf,
|
||||
}
|
||||
|
||||
@@ -31,6 +62,8 @@ pub struct Workspace {
|
||||
/// closed — the last element is the name already present in `visiting`.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum WorkspaceLoadError {
|
||||
/// File I/O failed while reading a module file (typically: file
|
||||
/// missing, permission denied).
|
||||
#[error("io error for {path}: {source}")]
|
||||
Io {
|
||||
path: PathBuf,
|
||||
@@ -38,6 +71,9 @@ pub enum WorkspaceLoadError {
|
||||
source: std::io::Error,
|
||||
},
|
||||
|
||||
/// File contents failed to parse as a [`Module`] or had the wrong
|
||||
/// schema tag. Wraps a [`CoreError`] (the single-module loader's
|
||||
/// error type).
|
||||
#[error("schema/parse error in {path}: {source}")]
|
||||
Schema {
|
||||
path: PathBuf,
|
||||
@@ -45,9 +81,14 @@ pub enum WorkspaceLoadError {
|
||||
source: CoreError,
|
||||
},
|
||||
|
||||
/// An `import { module: "foo" }` did not resolve to an existing
|
||||
/// file at the expected path.
|
||||
#[error("module `{name}` not found (expected at {expected_path})")]
|
||||
ModuleNotFound { name: String, expected_path: PathBuf },
|
||||
|
||||
/// The `name` field in a loaded module file disagrees with the
|
||||
/// file-stem-derived name the loader expected (i.e. the
|
||||
/// `<name>.ail.json` convention is broken).
|
||||
#[error(
|
||||
"module name in file ({name_in_file:?}) does not match expected name from path ({name_from_path:?})"
|
||||
)]
|
||||
@@ -56,9 +97,15 @@ pub enum WorkspaceLoadError {
|
||||
name_from_path: String,
|
||||
},
|
||||
|
||||
/// An import cycle was detected. `path` is the chain of module
|
||||
/// names in which the cycle was closed — the last element is the
|
||||
/// name that was already on the visit stack.
|
||||
#[error("import cycle detected: {}", path.join(" -> "))]
|
||||
Cycle { path: Vec<String> },
|
||||
|
||||
/// A module was reachable through two import paths, and its
|
||||
/// on-disk content (compared via [`module_hash`]) differs between
|
||||
/// the two reads. This typically means the file changed mid-load.
|
||||
#[error(
|
||||
"module `{name}` was loaded twice with differing content (hashes differ)"
|
||||
)]
|
||||
|
||||
Reference in New Issue
Block a user