# 帳票設計書 1-DScanner診断レポート

## 概要

本ドキュメントは、serve-dのDScanner診断レポート機能の設計を記述する。D言語ソースコードの静的解析結果を診断情報（Diagnostics）としてLSPクライアント（エディタ）に出力する帳票である。

### 本帳票の処理概要

DScanner診断レポートは、D言語ソースファイルの静的解析を実施し、コード品質に関する問題をリアルタイムにエディタ上に表示するための帳票である。

**業務上の目的・背景**：D言語開発者がコーディング中にソースコードの品質問題を早期に発見・修正できるようにするため、静的解析ツールDScannerをLSP経由で統合する。未使用変数、非推奨構文、インポート順序違反など、コンパイルエラーにはならないが品質上問題となるパターンを検出し、開発効率の向上とコード品質の維持を図る。

**帳票の利用シーン**：エディタでD言語ソースファイルを編集・保存した際に自動的に静的解析が実行され、検出された問題がエディタの診断パネルやインライン表示として即座にフィードバックされる。開発者はコードレビュー前に自身のコードの問題点を把握できる。

**主要な出力内容**：
1. 診断メッセージ（未使用変数、非推奨構文、インポート順序問題等の検出結果）
2. 診断の重大度（error / warning / hint）
3. 問題箇所のテキスト範囲（行・列情報）
4. 診断コード（DScannerのチェックキー名）
5. 関連情報（supplemental情報として付加的な位置・メッセージ）
6. 自動修正データ（autofixes: CodeReplacementまたはResolveContext）

**帳票の出力タイミング**：エディタでD言語ファイルが開かれた時、またはファイル内容が変更された時にリアルタイムで自動出力される。LSP `textDocument/publishDiagnostics` 通知として送信される。

**帳票の利用者**：D言語開発者（エディタを使用してコーディングを行うプログラマ）

## 帳票種別

診断レポート（LSP Diagnostics通知）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| - | エディタ診断パネル | LSP `textDocument/publishDiagnostics` | ファイル編集・保存時に自動出力 |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | JSON-RPC（LSPプロトコル） |
| 用紙サイズ | N/A（デジタル出力） |
| 向き | N/A |
| ファイル名 | N/A（ストリーム出力） |
| 出力方法 | LSP textDocument/publishDiagnostics通知 |
| 文字コード | UTF-8 |

### PDF固有設定

N/A（本帳票はPDF出力ではない）

### Excel固有設定

N/A（本帳票はExcel出力ではない）

## 帳票レイアウト

### レイアウト概要

LSP PublishDiagnosticsParams構造体として、ドキュメントURIと診断情報の配列を出力する。

```
┌─────────────────────────────────────┐
│         PublishDiagnosticsParams      │
├─────────────────────────────────────┤
│  uri: DocumentUri                    │
│  diagnostics: Diagnostic[]           │
│    ├─ range: TextRange               │
│    ├─ severity: DiagnosticSeverity   │
│    ├─ code: string (DScanner key)    │
│    ├─ source: "DScanner"|"serve-d"   │
│    ├─ message: string                │
│    ├─ tags: DiagnosticTag[]          │
│    ├─ relatedInformation[]           │
│    └─ data: {autofixes}              │
└─────────────────────────────────────┘
```

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | uri | 診断対象ドキュメントのURI | lint関数の引数document.uri | URI文字列 |

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | range | 問題箇所のテキスト範囲 | DScannerIssue.range -> getDscannerDiagnosticRange() | Position(line,character)のペア | - |
| 2 | severity | 診断の重大度 | adjustSeverityForType()による変換 | error/warning/hint | - |
| 3 | code | DScannerチェックキー | DScannerIssue.key | 文字列（例: "dscanner.suspicious.unused_variable"） | - |
| 4 | source | 診断ソース識別子 | 固定値 "DScanner" または "serve-d" | 文字列 | - |
| 5 | message | 診断メッセージ | DScannerIssue.description | 人間可読な説明文 | - |
| 6 | tags | 診断タグ | adjustSeverityForType()で設定 | unnecessary / deprecated | - |
| 7 | relatedInformation | 関連情報 | DScannerIssue.supplemental | Location + message の配列 | - |
| 8 | data | 自動修正データ | DScannerIssue.autofixes | checkName + autofixes JSON | - |

### フッター部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| - | N/A | 本帳票にフッター部は存在しない | - | - |

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| 言語ID | document.getLanguageId == "d" であること | Yes |
| リンティング有効 | config.d.enableLinting == true | Yes |
| 静的リンティング有効 | config.d.enableStaticLinting == true | Yes |
| 除外ファイルでないこと | isExcludedFile()がfalseであること | Yes |
| DScanner利用可能 | backend.getBestInstance!DscannerComponentが有効であること | Yes |
| @suppress未指定 | 対象行に@suppress(all)等の抑制注釈がないこと | No |
| ignoredKeysに未登録 | config.dscanner.ignoredKeysに含まれないこと | No |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | DScannerのissue出力順（ソースコード上の出現順） | 昇順 |

### 改ページ条件

N/A（ストリーム出力のため改ページなし）

## データベース参照仕様

### 参照テーブル一覧

本帳票はデータベースを使用しない。ソースコードのインメモリ解析結果を使用する。

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| N/A | - | - |

### テーブル別参照項目詳細

N/A

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| severity | issue.type == "error" ? error : warning（ただしunused_parameter/unused_variableはhint） | - | adjustSeverityForType()で決定 |
| source | issue.key.startsWith("workspaced") ? "serve-d" : "DScanner" | - | 行84-87 |
| range | getDscannerDiagnosticRange()によるバイトオフセット/行列変換 | - | index指定時とline/column指定時で分岐 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[ファイル編集/保存イベント] --> B[lint関数呼び出し]
    B --> C{言語ID == d ?}
    C -->|No| Z[終了]
    C -->|Yes| D[DscannerComponent取得]
    D --> E{インスタンス有効?}
    E -->|No| Z
    E -->|Yes| F{リンティング設定有効?}
    F -->|No| Z
    F -->|Yes| G{除外ファイル?}
    G -->|Yes| H[空の診断を設定して終了]
    G -->|No| I[DScanner INI取得]
    I --> J[DscannerComponent.lint実行]
    J --> K[issue一覧をループ処理]
    K --> L{ignoredKeys/suppress?}
    L -->|Yes| K
    L -->|No| M[Diagnostic構造体生成]
    M --> N[severity/source/range/tags設定]
    N --> O[結果配列に追加]
    O --> K
    K --> P[diagnosticsスロット0に格納]
    P --> Q[updateDiagnostics呼び出し]
    Q --> R[publishDiagnostics通知送信]
    R --> Z
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| インスタンス未取得 | DscannerComponentのインスタンスが取得できない | なし（静かに終了） | lint関数がreturnで終了 |
| 設定無効 | enableLintingまたはenableStaticLintingがfalse | なし（静かに終了） | lint関数がreturnで終了 |
| 除外ファイル | ファイルがexcludedFilesパターンに一致 | なし（空の診断を送信） | 診断をクリアして終了 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 1ファイルあたり0〜数十件の診断 |
| 目標出力時間 | ファイル編集後数百ミリ秒以内 |
| 同時出力数上限 | 1ファイルずつ逐次処理 |

## セキュリティ考慮事項

- 解析対象はローカルファイルシステム上のソースコードであり、個人情報を含まない
- DScanner INIファイルのパスはワークスペースディレクトリから解決される
- 外部ネットワークへのアクセスは行わない

## 備考

- DiagnosticSlotは0番が割り当てられている（dscanner.d 行32）
- デフォルト設定ではcould_be_immutable_checkとundocumented_declaration_checkが無効化されている（行26-29）
- @suppress注釈やstfu末尾コメントにより、個別の診断を抑制可能（行69-72）
- autofixes機能により、一部の問題はエディタ上で自動修正が提供される

---

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

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

### 推奨読解順序

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

まず、診断情報がどのようなデータ構造で表現されるかを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | diagnosticmanager.d | `source/served/linters/diagnosticmanager.d` | DiagnosticCollectionの定義（行10-11）、3つの診断プロバイダスロットの管理方式 |
| 1-2 | protoext.d | `source/served/lsp/protoext.d` | DScannerIniSection/DScannerIniFeature構造体（行257-277）、DScanner設定のデータ表現 |

**読解のコツ**: D言語のenum宣言（`enum DiagnosticSlot = 0;`）はコンパイル時定数であり、テンプレートパラメータとして使用される。`createDiagnosticsFor!DiagnosticSlot`はテンプレートインスタンス化。

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

処理の起点となる`lint`関数を特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | dscanner.d | `source/served/linters/dscanner.d` | lint関数（行34-96）が全体の処理フローを制御 |

**主要処理フロー**:
1. **行36-37**: 言語IDチェック（"d"でなければ即リターン）
2. **行39-42**: DscannerComponentの最適インスタンス取得
3. **行44-46**: リンティング設定の有効/無効チェック
4. **行48-52**: 除外ファイルチェック（globパターンマッチ）
5. **行56-58**: DScanner INI設定取得とlint実行
6. **行61-92**: 検出されたissueをDiagnostic構造体に変換するループ
7. **行94-95**: 結果をDiagnosticSlot 0に格納し、updateDiagnosticsを呼び出す

#### Step 3: 診断変換処理を理解する

DScanner issueからLSP Diagnosticへの変換処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | dscanner.d | `source/served/linters/dscanner.d` | convertFromWorkspaced関数（行135-152）: range変換、severity設定、relatedInformation構築、autofix変換 |
| 3-2 | dscanner.d | `source/served/linters/dscanner.d` | getDscannerDiagnosticRange関数（行216-235）: バイトオフセットまたは行/列からTextRangeへの変換 |
| 3-3 | dscanner.d | `source/served/linters/dscanner.d` | adjustSeverityForType関数（行237-250）: unused系はhint+unnecessaryタグ、それ以外はerror/warning |

**主要処理フロー**:
- **行139**: getDscannerDiagnosticRangeでテキスト範囲を算出
- **行140**: adjustSeverityForTypeで重大度とタグを決定
- **行141-146**: supplemental情報をDiagnosticRelatedInformationに変換
- **行147-151**: autofixesが存在する場合、data JSONに変換して格納

#### Step 4: 診断配信処理を理解する

診断結果がエディタにどのように送信されるかを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | diagnosticmanager.d | `source/served/linters/diagnosticmanager.d` | updateDiagnostics関数（行59-89）: 3プロバイダの診断を統合してLSP通知送信 |
| 4-2 | diagnosticmanager.d | `source/served/linters/diagnosticmanager.d` | combineDiagnostics関数（行16-37）: 同一URIの診断をマージ |

**主要処理フロー**:
- **行59-72**: combineDiagnosticsで全プロバイダの診断を統合後、publishDiagnostics通知を送信
- **行74-88**: 以前に公開されたURIで現在診断が無いものには空の診断を送信（クリア処理）

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

```
lint(Document document)                              [dscanner.d:34]
    |
    +-- backend.getBestInstance!DscannerComponent()   [dscanner.d:39]
    +-- config(document.uri)                          [dscanner.d:44]
    +-- isExcludedFile()                              [dscanner.d:48]
    |       +-- workspace(uri)
    |       +-- globMatch(file, excludedFile)          [dscanner.d:109]
    +-- getDscannerIniForDocument()                   [dscanner.d:56]
    |       +-- buildPath(instance.cwd, "dscanner.ini") [dscanner.d:129]
    +-- DscannerComponent.lint()                      [dscanner.d:57]
    +-- [foreach issue] convertFromWorkspaced()       [dscanner.d:75]
    |       +-- getDscannerDiagnosticRange()          [dscanner.d:139]
    |       +-- adjustSeverityForType()               [dscanner.d:140]
    |       +-- toJson(DScannerAutoFix)               [dscanner.d:148-151]
    +-- createDiagnosticsFor!0(uri)                   [dscanner.d:94]
    +-- updateDiagnostics(uri)                        [dscanner.d:95]
            +-- combineDiagnostics()                  [diagnosticmanager.d:16]
            +-- rpc.send(publishDiagnostics)          [diagnosticmanager.d:68-69]
```

### データフロー図

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

D言語ソースコード -------> DscannerComponent.lint()  ----> DScannerIssue[]
(document.rawText)         (静的解析エンジン)

DScannerIssue[] ---------> convertFromWorkspaced()   ----> Diagnostic[]
                           adjustSeverityForType()
                           getDscannerDiagnosticRange()

dscanner.ini設定 --------> getDscannerIniForDocument() --> StaticAnalysisConfig

Diagnostic[] ------------> createDiagnosticsFor!0()  ----> DiagnosticCollection
                           updateDiagnostics()
                           combineDiagnostics()

DiagnosticCollection ----> rpc.send()                ----> LSP publishDiagnostics通知
                                                          (エディタに表示)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| dscanner.d | `source/served/linters/dscanner.d` | ソース | DScanner診断の主処理（lint関数、変換関数群） |
| diagnosticmanager.d | `source/served/linters/diagnosticmanager.d` | ソース | 診断情報の統合管理・配信 |
| types.d | `source/served/types.d` | ソース | 共通型定義、Configuration構造体 |
| protoext.d | `source/served/lsp/protoext.d` | ソース | LSP拡張プロトコル型定義 |
| extension.d | `source/served/extension.d` | ソース | LSPサーバー拡張機能の登録・設定管理 |
