# 機能設計書 35-DScanner静的解析

## 概要

本ドキュメントは、D-Scannerを使用したソースコードの静的解析を実行し、診断結果（警告・エラー・ヒント）をクライアントに返却する機能の設計を記述する。

### 本機能の処理概要

**業務上の目的・背景**：D言語ソースコードの品質を向上させるため、D-Scannerによる静的解析（スタイルチェック、未使用変数検出、構文エラー検出等）をリアルタイムに実行し、問題箇所を診断結果としてエディタに表示する。これにより開発者はコンパイル前に潜在的な問題を発見・修正できる。

**機能の利用シーン**：テキストドキュメントの変更時（textDocument/didChange）にデバウンス付きで自動実行される。また、テキストドキュメントの保存時（textDocument/didSave）にも実行される。手動トリガー（served/doDscanner）も可能。

**主要な処理内容**：
1. ドキュメントの言語がD言語であることを確認する
2. 設定でリンティングが有効か確認する（enableLinting, enableStaticLinting）
3. 除外ファイルリストとの照合を行う
4. DscannerComponentの `lint` メソッドを呼び出し、静的解析を実行する
5. 解析結果を@suppress指示およびignoreKeys設定でフィルタリングする
6. 診断結果をDiagnostic型に変換し、診断マネージャ経由でクライアントに配信する

**関連システム・外部連携**：DscannerComponent（workspace-dサブパッケージ）がD-Scannerの解析エンジンをラップしている。診断結果はdiagnosticmanagerモジュール経由で `textDocument/publishDiagnostics` 通知としてクライアントに送信される。

**権限による制御**：設定項目 `d.enableLinting`（デフォルトtrue）と `d.enableStaticLinting`（デフォルトtrue）の両方がtrueの場合のみ実行される。除外ファイルリスト（dscanner.excludedFiles）でファイル単位の除外が可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 34 | DScanner リント（served/doDscanner） | 主画面 | D-Scannerを使用した静的解析を実行し診断結果を返却 |
| 35 | dfmt リント | 参照画面 | DScanner診断と共に診断マネージャで統合される |
| 36 | DUB ビルドリント | 参照画面 | DScanner診断と共に診断マネージャで統合される |

## 機能種別

静的解析処理（ソースコード解析・診断生成）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| document | Document | Yes | 解析対象のテキストドキュメント | 言語IDが "d" であること |

### 入力データソース

DocumentManagerから取得したドキュメントオブジェクト。テキストドキュメントの変更・保存イベントからトリガーされる。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| Diagnostic.range | TextRange | 問題箇所のテキスト範囲 |
| Diagnostic.severity | DiagnosticSeverity | 重大度（error/warning/hint） |
| Diagnostic.source | string | 診断ソース（"DScanner" or "serve-d"） |
| Diagnostic.message | string | 診断メッセージ |
| Diagnostic.code | JsonValue | 診断キー（例: "dscanner.suspicious.unused_variable"） |
| Diagnostic.tags | DiagnosticTag[] | タグ（例: unnecessary） |
| Diagnostic.relatedInformation | DiagnosticRelatedInformation[] | 関連情報 |
| Diagnostic.data | JsonValue | 自動修正用データ（autofixがある場合） |

### 出力先

診断マネージャ（DiagnosticSlot=0）経由で `textDocument/publishDiagnostics` 通知としてLSPクライアントに送信される。

## 処理フロー

### 処理シーケンス

```
1. 言語チェック
   └─ document.getLanguageId != "d" の場合は処理終了
2. DscannerComponentインスタンス取得
   └─ backend.getBestInstance!DscannerComponent で取得
   └─ 取得できない場合は処理終了
3. 設定チェック
   └─ config.d.enableLinting && config.d.enableStaticLinting の両方がtrueか確認
   └─ falseの場合は処理終了
4. 除外ファイルチェック
   └─ isExcludedFile でdscanner.excludedFilesとglob照合
   └─ 除外対象の場合は診断をクリアして終了
5. DScanner解析実行
   └─ instance.get!DscannerComponent.lint() を呼び出し
   └─ dscanner.iniの設定ファイルパスを取得（getDscannerIniForDocument）
   └─ servedDefaultDscannerConfig（could_be_immutable_check等を無効化）を適用
6. 結果フィルタリング
   └─ ignoredKeysに含まれるissueをスキップ
   └─ @suppress(key) / @suppress(all) / "stfu" コメントがある行をスキップ
7. Diagnostic変換
   └─ convertFromWorkspaced でrange/severity/relatedInformation/autofixデータを変換
   └─ adjustSeverityForType で unused_parameter/unused_variable をhintに降格
   └─ source を "DScanner" or "serve-d"（workspaced系）に設定
8. 診断マネージャに配信
   └─ createDiagnosticsFor!DiagnosticSlot(document.uri) に結果を格納
   └─ updateDiagnostics(document.uri) で全診断プロバイダの結果を統合・送信
```

### フローチャート

```mermaid
flowchart TD
    A[lint呼び出し] --> B{言語がD?}
    B -->|No| Z[処理終了]
    B -->|Yes| C{DscannerComponent利用可能?}
    C -->|No| Z
    C -->|Yes| D{リンティング有効?}
    D -->|No| Z
    D -->|Yes| E{除外ファイル?}
    E -->|Yes| F[診断クリア]
    F --> Z
    E -->|No| G[DScanner解析実行]
    G --> H[結果フィルタリング]
    H --> I[Diagnostic変換]
    I --> J[診断マネージャに配信]
    J --> Z
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-35-01 | D言語限定 | D言語ファイルのみ解析対象 | 常時 |
| BR-35-02 | 設定依存 | enableLintingとenableStaticLintingの両方がtrueの場合のみ実行 | 常時 |
| BR-35-03 | 除外ファイル | dscanner.excludedFilesとglobマッチするファイルは除外 | 除外設定がある場合 |
| BR-35-04 | @suppress | ソースコード行に@suppress(key)があればその診断をスキップ | 常時 |
| BR-35-05 | stfu | 行末が "stfu" の場合は全診断をスキップ | 常時 |
| BR-35-06 | デフォルト無効チェック | could_be_immutable_checkとundocumented_declaration_checkはデフォルト無効 | 常時 |
| BR-35-07 | 重大度調整 | unused_parameter/unused_variableはhintに降格しunnecessaryタグ付与 | 常時 |

### 計算ロジック

特になし。

## データベース操作仕様

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

本機能にデータベース操作は存在しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | コンポーネント未初期化 | DscannerComponentが利用不可 | 処理終了（診断なし） |
| - | 構文エラー | D-Scannerが構文解析でエラーを検出 | errorレベルのDiagnosticとして出力 |

### リトライ仕様

リトライは行わない。

## トランザクション仕様

トランザクション管理は不要。

## パフォーマンス要件

テキスト変更時のデバウンス遅延: ファイルサイズ50KB未満は200ms、50KB以上は1000ms（extension.d 1179行目）。保存時は即座に実行。

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

特になし。ローカルファイルの解析のみ。

## 備考

- DiagnosticSlotは0（DScanner用）。DUBビルド診断はSlot 1、dfmtリントはSlot 2を使用する。
- D-Scannerの設定ファイル（dscanner.ini）はインスタンスのcwdから検索され、存在しない場合はデフォルトの "dscanner.ini" が使用される。
- 自動修正（autofix）データがある場合はDiagnostic.dataにcheckNameとautofixesが格納される。

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | dscanner.d | `source/served/linters/dscanner.d` | DiagnosticSlot=0（32行目）、DScannerDiagnosticSource/SyntaxHintDiagnosticSource定数（22-23行目）、servedDefaultDscannerConfig（26-29行目）を確認 |
| 1-2 | diagnosticmanager.d | `source/served/linters/diagnosticmanager.d` | NumDiagnosticProviders=3（9行目）、diagnostics配列（11行目）を確認 |

**読解のコツ**: 3つの診断プロバイダ（DScanner=0, DUB=1, dfmt=2）がDiagnosticSlotで区別され、diagnosticmanagerで統合される。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | dscanner.d | `source/served/linters/dscanner.d` | `lint` 関数（34-96行目）がメインのリント処理 |

**主要処理フロー**:
1. **36-37行目**: 言語チェック（D言語のみ）
2. **39-41行目**: DscannerComponentインスタンス取得
3. **44-46行目**: 設定チェック（enableLinting, enableStaticLinting）
4. **48-52行目**: 除外ファイルチェック（isExcludedFile）
5. **56行目**: dscanner.ini取得（getDscannerIniForDocument）
6. **57-58行目**: D-Scanner解析実行
7. **61-92行目**: 結果フィルタリング・Diagnostic変換ループ
8. **94-95行目**: 診断マネージャへの配信

#### Step 3: 補助関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | dscanner.d | `source/served/linters/dscanner.d` | `isExcludedFile`（98-113行目）、`getDscannerIniForDocument`（121-133行目）、`convertFromWorkspaced`（135-152行目）、`adjustSeverityForType`（237-250行目） |

**主要処理フロー**:
- **98-113行目**: ワークスペースルートからの相対パスでglob照合
- **121-133行目**: インスタンスcwd/dscanner.iniの存在チェック
- **135-152行目**: DScannerIssueからDiagnosticへの変換（range, severity, relatedInformation, autofix data）
- **237-250行目**: unused_parameter/unused_variableをhintに降格

#### Step 4: トリガー元を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | extension.d | `source/served/extension.d` | `doDscanner`（1167-1187行目）がデバウンス制御付きでlintを呼び出す |
| 4-2 | extension.d | `source/served/extension.d` | `onDidChangeDocument`（1148-1165行目）がdoScannerを呼び出す |
| 4-3 | extension.d | `source/served/extension.d` | `onDidSaveDocument`（1316-1345行目）が保存時にlintを呼び出す |

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

```
onDidChangeDocument (extension.d:1148)
    └─ doDscanner (extension.d:1167)
           └─ setTimeout (デバウンス: 200ms/1000ms)
                  └─ dscanner.lint (dscanner.d:34)
                         |
                         +-- isExcludedFile (dscanner.d:98)
                         +-- getDscannerIniForDocument (dscanner.d:121)
                         +-- DscannerComponent.lint() (解析実行)
                         +-- convertFromWorkspaced (dscanner.d:135)
                         +-- adjustSeverityForType (dscanner.d:237)
                         +-- createDiagnosticsFor (diagnosticmanager.d:43)
                         +-- updateDiagnostics (diagnosticmanager.d:59)

onDidSaveDocument (extension.d:1316)
    └─ dscanner.lint (dscanner.d:34)
           └─ (同上)
```

### データフロー図

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

Document            ──▶  dscanner.lint()               ──▶  textDocument/publishDiagnostics
  .rawText                |                                    (RPC通知)
  .uri                    +-- DscannerComponent.lint()
                          |     (DScannerIssue[])
                          +-- @suppress フィルタ
                          +-- ignoredKeys フィルタ
                          +-- convertFromWorkspaced()
                          +-- adjustSeverityForType()
                          +-- diagnosticmanager
                                (DiagnosticSlot=0)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| dscanner.d | `source/served/linters/dscanner.d` | ソース | DScanner静的解析メイン処理 |
| diagnosticmanager.d | `source/served/linters/diagnosticmanager.d` | ソース | 複数診断プロバイダの統合・配信 |
| extension.d | `source/served/extension.d` | ソース | doDscanner/onDidChangeDocument/onDidSaveDocumentトリガー |
| types.d | `source/served/types.d` | ソース | 設定項目（enableLinting, enableStaticLinting等） |
| dscanner.d (workspace-d) | `workspace-d/source/workspaced/com/dscanner.d` | ソース | DscannerComponent実装（D-Scannerラッパー） |
