Skip to content

Linter 架构

本文来自 @leaysgur,首发于 leaysgur.github.io/posts

apps/oxlint

可执行文件来自 apps/oxlint crate 的 main.rs

Cargo.toml 配置入口

入口解析 CLI 参数后交由 LintRunner

Lint runner 流水线

crates/oxc_diagnostics

LintServicempsc::channel::Sender 交给 oxc_diagnostics,以异步接收 Lint 产物。

Lint 结果汇入诊断服务

随后由 miette crate 格式化输出:

miette 美化诊断

crates/oxc_linter

起点是 LintService

  • 内部持有 Arc<Runtime>
  • Runtime 列出待 Lint 的路径
  • 运行时使用 rayon 并行迭代路径
  • 结束后发送哨兵 None 结束信道

LintService 实现摘录

Runtime::process_path()

  • 根据扩展名和内容类型判断是否处理文件
  • .[m|c]?[j|t]s / .[j|t]sx 等后缀
  • .vue/.astro/.svelte 仅对部分 script 块提供支持(实验性)
  • 对源码执行 Lint,并把结果交给 DiagnosticService

process_path 细节

Runtime::process_source()

  • 使用 Parser 将源码解析为 AST
  • 经由 SemanticBuilder 组装 LintContext,再喂给 Linter

process_source

crates/oxc_semantic::SemanticBuilder

SemanticBuilder 会根据源码推导符号、节点、类等语义信息:

SemanticBuilder 主体

包括但不限于:

  • source_textnodesclassesscopes

  • trivias(注释)、jsdoc,等等

最终会产出 SemanticBuilderReturn,但交给 LintContext 的只有 Semantic

SemanticBuilderReturn

crates/oxc_linter::LintContext

LintContext

Semantic 为核心,对各种信息暴露 getter;diagnostic() 等方法供规则报错。

crates/oxc_linter::Linter

Linter 定义

run() 是整条流水线核心:

  • self.rules 持有待执行集合
  • 每条规则按需实现 Trait 定义的若干触发回调
  • 三类回调按顺序逐个执行

已实现规则请参阅:

rules.rs

新增规则后别忘了更新注册表。

最小 Demo

仓库提供从零搭建 Linter 的示例:

Minimal Linter 示例源码