# 機能設計書 32-未使用ローカル (unused-local)

## 概要

本ドキュメントは、Lua Language Serverにおける「未使用ローカル変数検出機能」の設計詳細を記載したものである。宣言されたが一度も参照されないローカル変数を検出し、コードの品質向上と不要なメモリ消費の削減を支援する診断機能を提供する。

### 本機能の処理概要

**業務上の目的・背景**：
ローカル変数を宣言したものの使用しない場合、コードの可読性低下やメンテナンス性の悪化を招く。また、変数名のタイプミスにより意図しない新しい変数が作成されている可能性もある。本機能は未使用のローカル変数を検出し、開発者に警告することで、コードの整理と潜在的バグの早期発見を支援する。

**機能の利用シーン**：
- コードエディタでLuaファイルを編集中にリアルタイムで診断結果が表示される
- コードレビュー時に不要な変数宣言を特定する
- リファクタリング時に削除可能な変数を見つける

**主要な処理内容**：
1. 対象ファイルのASTを取得する
2. AST内の`local`タイプのノードを走査する
3. 各ローカル変数の参照状況を`hasGet`関数で分析する
4. 特殊ケース（アンダースコア変数、to-be-closed、doc.classなど）を除外する
5. 未使用と判定されたローカル変数とその参照箇所を診断として報告する

**関連システム・外部連携**：
- LSP (Language Server Protocol)クライアント（VSCode等のエディタ）との連携
- 診断結果はLSPの`textDocument/publishDiagnostics`通知で送信される

**権限による制御**：
- 設定`Lua.diagnostics.disable`で本診断を無効化可能
- 設定`Lua.diagnostics.unusedLocalExclude`でパターンマッチによる除外が可能

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 26 | 診断情報 | 補助機能 | 未使用のローカル変数を検出 |
| 38 | 診断チェック | 補助機能 | 未使用ローカル変数の検出 |

## 機能種別

バリデーション / 静的解析 / 診断

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| uri | string | Yes | 診断対象ファイルのURI | 有効なファイルURIであること |
| callback | function | Yes | 診断結果を受け取るコールバック関数 | - |

### 入力データソース

- ファイルシステムから読み込まれたLuaソースコード
- `files.getState(uri)`から取得されるAST情報
- `config.get(uri, 'Lua.diagnostics.unusedLocalExclude')`から除外パターン

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| start | integer | エラー位置の開始オフセット |
| finish | integer | エラー位置の終了オフセット |
| tags | table | DiagnosticTag.Unnecessaryを含む配列 |
| message | string | 診断メッセージ（例：「未使用のローカル `xxx`」） |

### 出力先

- LSPクライアントへの診断通知（`textDocument/publishDiagnostics`）
- エディタ上でのインライン警告表示（淡色表示）

## 処理フロー

### 処理シーケンス

```
1. ファイル状態の取得
   └─ files.getState(uri)でAST付きの状態を取得
2. メタファイル判定
   └─ vm.isMetaFile(uri)でメタファイルかチェック
3. 除外パターンの取得
   └─ config.get(uri, 'Lua.diagnostics.unusedLocalExclude')
4. localノードの走査
   └─ guide.eachSourceType(ast.ast, 'local', callback)
5. 除外条件のチェック
   └─ アンダースコア/ENVMode/to-be-closed/doc.class/除外パターン
6. 参照状況の分析
   └─ hasGet(source)で'strong'/'weak'/nilを判定
7. 未使用判定と診断報告
   └─ 変数とその参照箇所両方を報告
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[ファイル状態を取得]
    B --> C{状態が存在するか?}
    C -->|No| D[終了]
    C -->|Yes| E[メタファイル・除外パターン取得]
    E --> F[localノードを走査]
    F --> G{除外対象か?}
    G -->|Yes| H[次のノードへ]
    G -->|No| I[hasGet分析]
    I --> J{参照あり?}
    J -->|strong| H
    J -->|weak| K{自分のテーブルか?}
    K -->|No| H
    K -->|Yes| L[診断結果を報告]
    J -->|nil| L
    L --> M[参照箇所も報告]
    M --> H
    H --> N{全ノード処理完了?}
    N -->|No| F
    N -->|Yes| D
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-32-01 | アンダースコア除外 | 変数名が`_`の場合は診断対象外 | プレースホルダーとして意図的に使用 |
| BR-32-02 | ENVMode除外 | 変数名がENVModeと一致する場合は除外 | _ENVなどの環境変数 |
| BR-32-03 | to-be-closed除外 | `<close>`属性を持つ変数は除外 | リソース管理用変数 |
| BR-32-04 | doc.class除外 | `---@class`アノテーションが付与された変数は除外 | 型定義用変数 |
| BR-32-05 | メタファイル関数パラメータ | メタファイル内の空関数パラメータは除外 | 宣言目的のパラメータ |
| BR-32-06 | weak参照判定 | setmethod/setfield/setindexのみの参照は弱い参照 | テーブル構築パターン |
| BR-32-07 | 自己テーブル判定 | weak参照でvalue.type == 'table'の場合のみ警告 | テーブルリテラル代入 |

### 計算ロジック

#### hasGet関数のロジック

```lua
-- 参照配列がない場合: nil（未使用）
-- getlocalがあり、nextがない場合: 'strong'（使用済み）
-- getlocalがあり、nextがsetmethod/setfield/setindexの場合: 'weak'
-- 上記以外: nil
```

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

本機能はデータベースを使用しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | ファイル状態なし | files.getState(uri)がnilを返す場合 | 処理を中断して正常終了 |

### リトライ仕様

リトライは行わない。ファイルが再度編集された際に自動的に再診断される。

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

該当なし（読み取り専用処理）

## パフォーマンス要件

- 単一ファイルの診断は通常0.5秒以内に完了すること
- 大量のローカル変数を持つファイルでも適切に処理できること

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

- ユーザーコードの読み取りのみを行い、ファイルシステムへの書き込みは行わない
- 外部ネットワークへのアクセスは行わない

## 備考

- `DiagnosticTag.Unnecessary`タグにより、エディタ上で該当コードが淡色表示される
- 参照箇所も同時に報告するため、使用箇所全体が視覚的にマークされる

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | define.lua | `script/proto/define.lua` | DiagnosticTag.Unnecessaryの定義（31行目） |
| 1-2 | guide.lua | `script/parser/guide.lua` | localノードの構造、ref配列の意味 |

**読解のコツ**: `source.ref`配列には、そのローカル変数への全参照（getlocal/setlocal）が格納される。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | init.lua | `script/core/diagnostics/init.lua` | 診断処理のメインループ |

**主要処理フロー**:
1. **119行目**: 動的にunused-local.luaをロード・実行
2. **131-133行目**: 結果にレベルとコードを付与

#### Step 3: 診断ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | unused-local.lua | `script/core/diagnostics/unused-local.lua` | 未使用ローカル検出のメインロジック |

**主要処理フロー**:
- **9-34行目**: `hasGet`関数 - 参照状況を'strong'/'weak'/nilで返す
- **36-42行目**: `isMyTable`関数 - テーブルリテラル代入かを判定
- **44-54行目**: `isToBeClosed`関数 - `<close>`属性の有無をチェック
- **56-66行目**: `isDocClass`関数 - `---@class`バインドの有無をチェック
- **69-75行目**: `isDeclareFunctionParam`関数 - 空関数のパラメータか判定
- **77-81行目**: ファイル状態取得
- **83-85行目**: メタファイル判定と除外パターン取得
- **86-129行目**: メインループでlocalノードを走査・判定・報告

#### Step 4: ヘルパー関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | glob.lua | `script/glob.lua` | パターンマッチング（除外パターン用） |
| 4-2 | vm.lua | `script/vm/vm.lua` | isMetaFile, isEmptyFunction |

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

```
script/core/diagnostics/init.lua (診断エントリー)
    │
    ├─ require('core.diagnostics.unused-local')
    │      │
    │      ├─ files.getState(uri)
    │      │
    │      ├─ vm.isMetaFile(uri)
    │      │
    │      ├─ config.get(uri, 'Lua.diagnostics.unusedLocalExclude')
    │      │
    │      ├─ glob.glob(ignorePatterns)
    │      │
    │      └─ guide.eachSourceType(ast.ast, 'local', callback)
    │             │
    │             ├─ isToBeClosed(source)
    │             ├─ isDocClass(source)
    │             ├─ isDeclareFunctionParam(source)
    │             ├─ hasGet(source)
    │             └─ isMyTable(source)
    │
    └─ callback(result)
```

### データフロー図

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

Luaソースコード ───▶ パーサー(AST生成) ───▶ AST
                                              │
                                              ▼
                         guide.eachSourceType('local')
                                              │
                                              ▼
                         除外条件チェック ───▶ 除外対象は無視
                                              │
                                              ▼
                         hasGet(source) ───▶ 参照状況分析
                                              │
                                              ▼
                         診断メッセージ生成 ───▶ LSP診断通知
                                              │        （Unnecessaryタグ付き）
                                              ▼
                                        エディタ淡色表示
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| unused-local.lua | `script/core/diagnostics/unused-local.lua` | ソース | 未使用ローカル診断のメインロジック |
| init.lua | `script/core/diagnostics/init.lua` | ソース | 診断処理のエントリーポイント |
| define.lua | `script/proto/define.lua` | ソース | DiagnosticTag定義 |
| vm.lua | `script/vm/vm.lua` | ソース | isMetaFile, isEmptyFunction |
| guide.lua | `script/parser/guide.lua` | ソース | AST操作ユーティリティ |
| files.lua | `script/files.lua` | ソース | ファイル状態管理 |
| glob.lua | `script/glob.lua` | ソース | パターンマッチング |
| config.lua | `script/config/config.lua` | ソース | 設定値取得 |
