# 诊断系统规范

本文件约定 Gonidium 的诊断（错误 / 警告 / 建议）规范，包括**严重性等级**与**短码格式**。实现位于 `diag.rs`，本文件是其稳定文档。

## 严重性等级

`diag::Severity` 从低到高依次为：

- **Allow**：完全抑制，不输出、不计入任何列表。
- **Info**：提示级信息，不影响编译流程。
- **Warning**：警告，默认不阻断编译；开启 `warnings_as_errors` 时会当作 Error 处理。
- **Error**：错误，编译阶段会终止或进入“错误收集”通道；可以通过 `DiagConfig` 升降级。
- **Fatal**：致命错误，**无法被配置或抑制**，一旦出现立即终止当前编译流程。

语义上：

- **Fatal**：通常对应语法层面或实现假设被破坏时的情况；用户无法通过配置让编译“带病继续”。
- **Error**：语义不合法，但在某些模式（IDE、容错编译）下可以通过配置调整为 Warning / Info。
- **Warning / Info**：优化、安全性、风格等软诊断，可按需调整或关闭。

## 诊断短码格式

每个 `DiagCode` 都有一个稳定的**短码**，用于 CLI 输出、测试夹具和文档索引。格式为：

```text
<PhasePrefix><S><NN>
```

- **`<PhasePrefix>`**：阶段前缀
  - `P`：parse（解析）
  - `L`：lower / IR
  - `O`：optimisation（优化）
- **`<S>`**：一位十进制数字，表示该诊断的**默认严重性**
  - `0`：Fatal
  - `1`：Error
  - `2`：Warning
  - `3`：Suggestion / Info
- **`<NN>`**：两位序号（`00`–`99`），在同一前缀 + 严重性下唯一。后续如需扩容可以演进为 3 位，不改变已有短码。

> 短码中的 `<S>` 只表示**内建默认严重性**。实际生效的严重性由 `DiagConfig` 决定（Fatal 除外）。

### 当前内建诊断一览（摘要）

> 详见 `diag.rs` 中 `DiagCode::short_code` 与 `DiagCode::default_severity` 的实现。

- **Parse（解析）**
  - `P001`：整数虚数字面量（例如 `2j`）不被支持，提示使用 `2.0j` 形式。（Fatal，不可抑制）
  - `P002`：未知类型名。（Fatal，不可抑制）
  - `P003`：语法错误。（Fatal，不可抑制）
  - `P101`：未定义变量（Error，可配置）。
  - `P102`：保留标识符被用作参数名或绑定名（Error，可配置）。

- **Lower / IR（降低）**
  - **Errors (`L1xx`)**
    - `L101`：未定义变量。
    - `L102`：类型不匹配 / 无法进行合法提升。
    - `L103`：整数字面量超出目标 dtype 范围。
    - `L104`：未定义函数 / 内建。
    - `L105`：函数调用参数个数不匹配。
    - `L106`：整数除零或取模零。
  - **Warnings (`L2xx`)**
    - `L201`：浮点字面量在目标 dtype 中可能精度丢失。
    - `L202`：`u64` 与有符号整数混合参与运算，可能发生截断或溢出。
    - `L203`：多余的 cast 链，建议收敛为单次 cast。
  - **Suggestions / Info (`L3xx`)**
    - `L301`：隐式类型提升（promotion）发生，记录最终 dtype 以便用户理解推导过程。

- **Optimisation（优化）**
  - **Warnings (`O2xx`)**
    - `O201`：常量折叠发生了可能不安全的运算（例如潜在溢出），优化器做了保守处理。
  - **Suggestions / Info (`O3xx`)**
    - `O301`：在优化后的 DAG 上发生了额外的类型提升（与 lowering 阶段的 `L301` 相呼应）。

- **Compose（表达式组合）**
  - **Warnings (`C2xx`)**
    - `C201`：组合多个表达式时发现同名符号，系统会将其视为同一变量；该行为可能易混淆。

## Help 提示（修复建议）

- 每个 `DiagCode` 都可以在 `diag.rs` 中定义一个稳定的默认 Help 文本（见 `DiagCode::default_help`），用于告诉用户**如何修改代码**来消除该诊断。
- 构造诊断时：
  - 如果调用方给 `Diag::new` 传入了 `Some(hint)`，则使用该上下文相关的提示。
  - 如果传入的是 `None`，则会自动回退到 `DiagCode::default_help()` 中为该短码预设的 Help 文本（如果有）。
- 在输出层面：
  - CLI / e2e 使用 `Diag::report_string` + `ariadne`，Help 文本会以 `help:` 段落展示。
  - REPL 使用内部的 `format_diag_line`，当存在 Help 时会额外输出一行：`help: ...`。

因此，**所有非 Allow 的错误 / 警告 / 建议（Info）默认都会携带一个 Help 提示**，用户可以直接根据提示修改代码或类型注解来修复问题。

## 配置与可变更性

- 使用 `DiagConfig` 可以为非 Fatal 的 `DiagCode` 设置新的严重性，或将其设为 `Allow` 完全关闭：

```rust
use gonidium::diag::{DiagCode, DiagConfig, Severity};

let cfg = DiagConfig::default()
    .set(DiagCode::LowerImplicitPromotion, Severity::Allow)  // 关闭隐式提升提示
    .set(DiagCode::LowerFloatPrecisionLoss, Severity::Error) // 将精度丢失升级为错误
    .with_warnings_as_errors();                              // 剩余所有 Warning 视为 Error
```

- 当 `warnings_as_errors` 为真时：
  - 所有 `Warning` 会被视为 `Error`；
  - 显式 per-code 覆盖优先于该全局开关；
  - **Fatal 不受任何配置影响**。

## 设计原则

- **稳定性**：一旦短码对外可见，就应避免复用或静默改变其语义；若必须废弃，应在文档中标记。
- **可分组性**：前缀 + 严重性位使得短码能够按阶段与严重性自然分组，方便过滤与统计。
- **可配置性**：除 Fatal 外，严重性均允许通过 `DiagConfig` 调整，以适应不同 CI / IDE / 实验场景。
