# 画面設計書 26-診断情報

## 概要

本ドキュメントは、Lua Language Serverにおける「診断情報」機能の画面設計書です。LSPプロトコルの `textDocument/diagnostic` リクエストに対応し、エラー・警告などの診断情報を表示します。

### 本画面の処理概要

診断情報機能は、Luaソースコードを解析し、構文エラー、型エラー、未定義変数、未使用変数、スペルミスなど、様々な問題を検出してエディタに表示する機能です。

**業務上の目的・背景**：コードの品質を維持するためには、問題を早期に発見することが重要です。診断情報機能により、コードを記述している最中にリアルタイムで問題を検出し、開発者にフィードバックを提供します。これにより、バグの早期発見、コードレビューの効率化、コーディング規約の遵守が促進されます。特にLuaは動的型付け言語であるため、型チェックによる事前のエラー検出が開発効率向上に貢献します。

**画面へのアクセス方法**：エディタ（VS Code等）でLuaファイルを開くと、自動的に診断処理が実行されます。ファイルの編集時、保存時にも診断が更新されます。診断結果はエディタの問題パネル、コード上の下線、ホバー時のツールチップで確認できます。

**主要な操作・処理内容**：
1. ファイルを開く、編集する、保存するなどのイベントが発生
2. LSPサーバーが診断処理を開始
3. 構文解析 → 型チェック → リンター → スペルチェックの順に処理
4. 検出された問題をDiagnostic配列としてエディタに通知（publishDiagnostics）
5. エディタが下線、問題パネル、ホバー等で診断結果を表示

**画面遷移**：診断結果からコードアクション（クイックフィックス）を呼び出し、問題を自動修正することが可能です（No.14 コードアクション、No.29 問題解決と連携）。

**権限による表示制御**：`Lua.diagnostics.enable` 設定がfalseの場合、診断情報は表示されません。また、ライブラリファイルや無視されたファイルに対する診断の有効/無効も設定可能です。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 30 | 構文診断 | 主機能 | エラー・警告などの診断情報を表示する主処理 |
| 31 | 未定義グローバル (undefined-global) | 補助機能 | 未定義のグローバル変数を検出 |
| 32 | 未使用ローカル (unused-local) | 補助機能 | 未使用のローカル変数を検出 |
| 33 | 未使用関数 (unused-function) | 補助機能 | 未使用の関数を検出 |
| 34 | 重複インデックス (duplicate-index) | 補助機能 | テーブルの重複キーを検出 |
| 35 | 型不一致 (type-mismatch) | 補助機能 | 型の不一致を検出 |
| 36 | 非推奨機能 (deprecated) | 補助機能 | 非推奨APIの使用を検出 |
| 37 | スペルチェック (spell-check) | 補助機能 | 識別子のスペルミスを検出 |
| 38 | コードスタイル (codestyle-check) | 補助機能 | コーディングスタイルの検証 |

## 画面種別

LSP機能（診断・問題表示）

## URL/ルーティング

LSPメソッド: `textDocument/diagnostic`
LSP通知: `textDocument/publishDiagnostics`

## 入出力項目

### 入力パラメータ（textDocument/diagnostic）

| 項目名 | 型 | 必須 | 説明 |
|--------|-----|------|------|
| textDocument | TextDocumentIdentifier | はい | 対象ドキュメントの識別子 |

### 出力パラメータ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| kind | string | 'full'（全結果）または 'unchanged'（変更なし） |
| resultId | string | 結果ID（キャッシュ用） |
| items | Diagnostic[] | 診断結果のリスト |

### Diagnostic構造

| 項目名 | 型 | 説明 |
|--------|-----|------|
| range | Range | 問題の範囲 |
| severity | DiagnosticSeverity | 重要度（Error=1, Warning=2, Information=3, Hint=4） |
| code | string | 診断コード（例：undefined-global） |
| source | string | ソース（Diagnostics） |
| message | string | エラーメッセージ |
| tags | DiagnosticTag[] | タグ（Unnecessary=1, Deprecated=2） |
| relatedInformation | DiagnosticRelatedInformation[] | 関連情報 |

## 表示項目

| 項目名 | 型 | 説明 |
|--------|-----|------|
| range | Range | 問題箇所の範囲（下線表示） |
| severity | DiagnosticSeverity | 重要度（赤=Error, 黄=Warning, 青=Info, 灰=Hint） |
| message | string | エラー・警告メッセージ |

## イベント仕様

### 1-ファイルオープン時診断

1. ユーザーがLuaファイルを開く
2. `textDocument/didOpen` 通知がサーバーに送信
3. サーバーが診断処理を開始
4. `textDocument/publishDiagnostics` 通知でエディタに結果を送信

### 2-ファイル編集時診断

1. ユーザーがコードを編集
2. `textDocument/didChange` 通知がサーバーに送信
3. サーバーが診断処理を更新（デバウンス処理あり）
4. `textDocument/publishDiagnostics` 通知で更新結果を送信

### 3-プル型診断（textDocument/diagnostic）

1. エディタが `textDocument/diagnostic` リクエストを送信
2. サーバーが `doDiagnostic` を実行
3. 結果を返却（現時点では 'unchanged' を返却）

## データベース更新仕様

### 操作別データベース影響一覧

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 診断情報要求 | - | - | データベース操作なし（読み取り専用） |

## メッセージ仕様

| メッセージID | 種別 | 表示内容 | 発生条件 |
|-------------|------|----------|----------|
| PARSER_* | エラー/警告 | 構文エラーメッセージ | パース時にエラーが検出された場合 |
| DIAG_* | 警告/情報 | 診断メッセージ | 各種診断で問題が検出された場合 |
| DIAG_NEED_VERSION | 情報 | バージョン互換性警告 | 特定のLuaバージョンでのみ有効な構文の場合 |

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| Lua.diagnostics.enable が false | 診断をスキップ |
| ワークスペース準備中 | 診断を遅延 |
| ライブラリファイル（Lua.diagnostics.libraryFiles設定依存） | 設定に応じて診断をスキップ |
| 無視されたファイル（Lua.diagnostics.ignoredFiles設定依存） | 設定に応じて診断をスキップ |

## 備考

- 診断結果はキャッシュされ、変更がない場合は再計算を省略
- `Lua.diagnostics.workspaceDelay` でワークスペース診断の遅延を設定可能
- `Lua.diagnostics.workspaceEvent` で診断トリガー（OnChange/OnSave）を設定可能
- `Lua.diagnostics.disable` で特定の診断を無効化可能
- `Lua.diagnostics.severity` で各診断の重要度を変更可能
- `---@diagnostic disable` コメントで行単位の診断無効化が可能

---

## コードリーディングガイド

本画面を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

LSPプロトコルで定義されるDiagnostic型とDiagnosticSeverity型の構造を理解することが重要です。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | define.lua | `script/proto/define.lua` | DiagnosticSeverity定義（Error=1, Warning=2等）を確認 |
| 1-2 | diagnostic.lua | `script/provider/diagnostic.lua` | Diagnostic構造の構築（86-123行目）を確認 |

**読解のコツ**: Diagnosticはrange, severity, code, message, tags, relatedInformationなどを持ちます。

#### Step 2: エントリーポイントを理解する

LSPリクエスト/通知ハンドラがどのように呼び出されるかを理解します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | provider.lua | `script/provider/provider.lua` | `textDocument/diagnostic` ハンドラ（1468-1507行目）を確認 |
| 2-2 | diagnostic.lua | `script/provider/diagnostic.lua` | ファイルイベント監視（678-705行目）を確認 |

**主要処理フロー**:
1. **1468行目**: `m.register 'textDocument/diagnostic'` でハンドラを登録
2. **1479行目**: URIを取得
3. **1480行目**: ワークスペース準備完了を待機
4. **1481-1483行目**: doDiagnosticを実行
5. **1485-1488行目**: 'unchanged' と resultId を返却

#### Step 3: 診断処理の実行を理解する

実際の診断処理を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | diagnostic.lua | `script/provider/diagnostic.lua` | doDiagnostic関数（281-358行目）を確認 |

**主要処理フロー**:
- **282-284行目**: isValid関数で診断対象かを確認
- **288-292行目**: ファイル状態の取得
- **301行目**: 構文エラーの収集（syntaxErrors）
- **337-354行目**: core診断の実行と結果の構築
- **325-329行目**: publishDiagnosticsで結果を通知

#### Step 4: 個別診断の実行を理解する

各診断チェックの実行を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | init.lua | `script/core/diagnostics/init.lua` | 診断ループ（172-191行目）を確認 |

**主要処理フロー**:
- **151-164行目**: 診断リストの構築（buildDiagList）
- **99-146行目**: check関数で個別診断を実行
- **119行目**: `require('core.diagnostics.' .. name)` で診断モジュールをロード

### プログラム呼び出し階層図

```
textDocument/diagnostic (provider.lua:1468-1507)
    │
    └─ doDiagnostic (provider/diagnostic.lua:281-358)
           │
           ├─ isValid(uri) - 診断対象確認
           │
           ├─ syntaxErrors(uri, ast) - 構文エラー収集
           │
           └─ core/diagnostics/init.lua (172-191行目)
                  │
                  ├─ check(uri, 'undefined-global', ...)
                  │      └─ core/diagnostics/undefined-global.lua
                  │
                  ├─ check(uri, 'unused-local', ...)
                  │      └─ core/diagnostics/unused-local.lua
                  │
                  └─ ... 各診断チェック
```

### データフロー図

```
[入力]                         [処理]                           [出力]

DiagnosticRequest {            1. 診断対象確認                  Diagnostic[] {
  textDocument                 2. 構文解析/エラー収集             [
}                              3. 各診断チェック実行               {
                                  ├─ undefined-global               range: {...},
ファイルイベント                   ├─ unused-local                   severity: 1,
(didOpen/didChange/didSave)       ├─ type-mismatch                  code: 'undefined-global',
    │                             └─ ... など                       message: 'Undefined global'
    │                          4. 結果マージ                       }
    └─────────────────────▶   5. publishDiagnostics             ]
                                                                }
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| provider.lua | `script/provider/provider.lua` | ソース | LSPリクエストハンドラの登録・処理 |
| diagnostic.lua | `script/provider/diagnostic.lua` | ソース | 診断処理のメイン制御 |
| init.lua | `script/core/diagnostics/init.lua` | ソース | 診断チェックの実行制御 |
| undefined-global.lua | `script/core/diagnostics/undefined-global.lua` | ソース | 未定義グローバル診断 |
| unused-local.lua | `script/core/diagnostics/unused-local.lua` | ソース | 未使用ローカル診断 |
| define.lua | `script/proto/define.lua` | ソース | DiagnosticSeverity等の定義 |
| converter.lua | `script/proto/converter.lua` | ソース | LSPプロトコル型変換 |
| config.lua | `script/config.lua` | ソース | 設定値の管理 |
