Original Note

rust-analyzer 架构文档示例:从结构到不变量

  • self_study_notes
  • Original Note
  • Updated: unknown
Source Collection
self_study_notes
Source Path
self_study_notes/harness/OpenAI Harness/整理版/architecture_example(整理版).md
Type
Original Note
Updated At
unknown

rust-analyzer 架构文档示例:从结构到不变量(整理版)

原始资料architecture_example(rust-analyzer 的 Architecture 文档摘录) 相关笔记:[ARCHITECTURE.md 写作方法](/en/original-notes/self_study_notes/harness/OpenAI Harness/整理版/ARCHITECTURE(整理版))、[OpenAI Harness:Agent-first 工程方法](/en/original-notes/self_study_notes/harness/OpenAI Harness/整理版/OpenAI Harness(整理版))、[本目录索引](/en/original-notes/self_study_notes/harness/OpenAI Harness/整理版/索引) 整理说明:原文是较长的英文架构文档。本版本保留其可迁移的架构表达方法与关键实例;具体 Rust API、版本和外部链接以原始笔记为准。

内容简要概括

rust-analyzer 的架构文档展示了一种强可读性的 Code Map 写法:先解释输入、派生语义模型与增量更新,再按模块说明职责、API Boundary 和不能被破坏的 invariants。它把测试、取消、错误处理、代码生成和 observability 作为横切关注点,使贡献者能够既定位模块,也理解系统级约束。这个实例说明好的架构文档不是目录清单,而是“模块职责 + 边界 + 演化约束”的组合。

rust-analyzer、Code Map、salsahiride、LSP、API Boundary、architectural invariants、incremental computation、cancellation、data-driven tests、observability

目录


1. 顶层模型:输入、派生状态与增量更新

文档先定义系统的基本计算模型:客户端提供源代码文件与项目结构(CrateGraph)作为 input / ground state;分析器在内存中按需推导得到已解析的模块、函数、类型和引用关系,即 derived state。客户端提交小变更时,系统只重新计算受影响的部分。

这一开场有三个效果:

  • 说明产品的核心职责:把代码转为可供 IDE 使用的语义模型;
  • 解释为什么内存、惰性计算和增量更新是架构中心,而非优化细节;
  • 为后续模块边界提供共同语言:输入查询、派生查询、语法、语义和 IDE 特性。

2. Code Map 的层次与边界

原文没有平铺罗列目录,而是按职责解释模块层级:

层级/模块 核心职责 文档强调的边界
xtask、编辑器插件、libs/ 构建辅助、编辑器集成、可发布独立库 与核心分析能力分开
parsersyntax 解析与语法树 syntax 独立于 salsa 和 LSP,是可复用 API Boundary
base_db 定义输入查询、承载增量计算基础设施 不知道 Cargo、文件系统路径等具体实现
hir_* 名称解析、宏展开、类型推断等语义计算 明确不是对外 API,且以增量性为中心
hir 对外提供静态、已解析的语义视图 将内部 ECS 风格 API 包装为较稳定的 façade
ide completion、goto definition 等 IDE 能力 面向编辑器概念的可序列化 API Boundary
rust-analyzer LSP 二进制入口与事件循环 唯一了解 LSP/JSON 与外部 I/O 的层
vfs、toolchain、宏、profile 等 文件系统、项目模型、宏、性能支持 隔离 OS 路径、构建工具或专门机制

这种写法既能帮助读者从任务反查位置,也能说明读者当前模块在系统中的角色。关于更通用的 Code Map 写法见 [ARCHITECTURE(整理版)](/en/original-notes/self_study_notes/harness/OpenAI Harness/整理版/ARCHITECTURE(整理版))。

3. 最值得迁移的架构不变量

原始示例反复使用 Architecture Invariant 标记,明确“哪些事情故意不存在”或“什么不能因局部改动被破坏”。可迁移的思维包括:

3.1 让输入与实现细节解耦

  • parser 与具体 token、语法树实现解耦;
  • base_db 不感知 Cargo 和真实文件路径,使用抽象的 CrateGraphFileId
  • vfs 不假设全局唯一文件系统。

这类规则防止上层能力被某个工具、路径或存储实现锁死。

3.2 把 API Boundary 画在稳定的抽象层

  • syntax 是仅依赖语法树即可使用的边界;
  • hir 将内部语义计算暴露为静态、已解析的视图;
  • ide 使用编辑器术语和可序列化数据,而不是泄漏内部语法/语义类型;
  • LSP/JSON 仅在最终 server 层出现。

稳定边界让内部可以演化,同时给调用方一个可理解、可测试的接口。

3.3 用失效范围约束增量系统

hir_* 的核心要求是:编辑函数 foo 的函数体不能使无关函数 bar 的全局派生数据失效。这把“增量性能”表达为可审阅的架构承诺,而不是笼统的性能愿望。

3.4 将不完整与失败视为一等场景

  • parser 产生 (T, Vec<Error>),而非一遇到错误就失败;
  • 语法树可不完整,调用方必须处理 Option
  • 服务器即使构建损坏也应尽量提供局部 IDE 能力;
  • 核心 ide/hir 不直接触碰外部世界,I/O 仅在 LSP 边缘发生。

这些约束特别适合长生命周期、交互式或 Agent 可操作的系统:失败应被隔离、观察和恢复,而不是放大成全局中断。

4. 横切关注点如何进入架构文档

原文将无法归属单一模块的系统能力单独成节,避免它们在目录说明中消失。

4.1 代码生成

生成代码应有统一入口、将产物提交或明确再生成方式,并用测试确认生成结果未过期;同时避免不必要的 bootstrap 依赖,降低构建链复杂度。

4.2 取消与快照

输入变化会使正在计算的结果过时。示例将取消传播到明确边界,在 ide 层捕获并转换为可处理的结果;这一模式把并发和交互响应性变成可说明的设计。

4.3 测试

测试围绕三个系统边界分层:外层 LSP 集成测试、中层 ide API 测试、内层 hir 查询测试。测试数据驱动、可复现、避免依赖外部资源;这样既覆盖边界又降低重构成本。

4.4 错误处理与 observability

核心计算避免 I/O 失败路径,外部请求则隔离 panic;显式事件循环、低成本 profiler 和对象计数工具帮助维护者理解长运行进程的行为。这对应 Agent-first 工程中“让 logs、metrics、traces 对 Agent 可读”的要求,详见 [OpenAI Harness(整理版)](/en/original-notes/self_study_notes/harness/OpenAI Harness/整理版/OpenAI Harness(整理版))。

5. 对编写自身 ARCHITECTURE.md 的启示

  1. 先以输入、输出、主流程建立全局模型,再讨论目录。
  2. 每个核心模块至少回答:负责什么、依赖谁、向谁暴露什么。
  3. 明确 API Boundary,以及哪些实现知识不应跨越该边界。
  4. 标注少量真正长期的 invariants,必要时用测试或 lint 强制。
  5. 单列 code generation、测试、错误处理、取消、性能/可观测性等横切机制。
  6. 不要复制此例的具体 Rust 细节;应把同样的表达框架映射到自己的领域与稳定约束。

原始笔记保留了完整的模块说明、命令和外部参考链接,需查阅具体实现时可直接回到 原文摘录

Evidence-backed relations

Source Note · Same Topic

切换到中文